├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── common.go ├── data └── test.session ├── decoder.go ├── decoder_test.go ├── doc.go ├── encoder.go ├── encoder_test.go ├── fuzz.go ├── fuzz_test.go ├── nut.json └── php_serialize ├── .gitignore ├── .travis.yml ├── README.md ├── common.go ├── common_helper.go ├── common_helper_test.go ├── php └── test.php ├── serialize.go ├── serialize_test.go ├── unserialize.go └── unserialize_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.nut 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Yuriy Vasiyarov, Maksim Naumov. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | php_session_decoder 2 | =================== 3 | 4 | PHP session encoder/decoder written in Go 5 | [![Build Status](https://secure.travis-ci.org/yvasiyarov/php_session_decoder.png?branch=master)](http://travis-ci.org/yvasiyarov/php_session_decoder) 6 | 7 | Installation 8 | ------------ 9 | 10 | Install: 11 | 12 | - ~~The recommended way~~ to install is using gonuts.io: 13 | 14 | 15 | nut get yvasiyarov/php_session_decoder 16 | for more information, please, go to the http://www.gonuts.io/yvasiyarov/php_session_decoder 17 | 18 | - Using default go get tool: 19 | 20 | 21 | go get github.com/yvasiyarov/php_session_decoder 22 | 23 | Getting started 24 | --------------- 25 | 26 | Example: load php session data from redis: 27 | 28 | if sessionId, err := req.Cookie("frontend"); err == nil { 29 | if sessionData, err := redis.Get("PHPREDIS_SESSION:" + sessionId.Value); err == nil { 30 | decoder := php_session_decoder.NewPhpDecoder(sessionData.String()) 31 | if sessionDataDecoded, err := decoder.Decode(); err == nil { 32 | //Do something with session data 33 | } 34 | } else { 35 | //Can not load session - it can be expired 36 | } 37 | } 38 | 39 | Example: Encode php session data: 40 | 41 | data := make(PhpSession) 42 | data["make some"] = " changes" 43 | encoder := NewPhpEncoder(data) 44 | if result, err := encoder.Encode(); err == nil { 45 | //Write data to redis/memcached/file/etc 46 | } 47 | 48 | Copyright 49 | ---------------------------- 50 | 2013-2014 Yuriy Vasiyarov 51 | 2014 Yuriy Vasiyarov, Maksim Naumov. 52 | 53 | All rights reserved. 54 | -------------------------------------------------------------------------------- /common.go: -------------------------------------------------------------------------------- 1 | package php_session_decoder 2 | 3 | import "github.com/yvasiyarov/php_session_decoder/php_serialize" 4 | 5 | const SEPARATOR_VALUE_NAME rune = '|' 6 | 7 | type PhpSession map[string]php_serialize.PhpValue 8 | -------------------------------------------------------------------------------- /data/test.session: -------------------------------------------------------------------------------- 1 | product_last_viewed|a:5:{i:689207;s:6:"689207";i:664225;s:6:"664225";i:744505;s:6:"744505";i:636827;s:6:"636827";i:695736;s:6:"695736";}core|a:4:{s:23:"_session_validator_data";a:4:{s:11:"remote_addr";s:13:"195.91.253.98";s:8:"http_via";s:0:"";s:20:"http_x_forwarded_for";s:0:"";s:15:"http_user_agent";s:104:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11";}s:13:"session_hosts";a:1:{s:12:"www.butik.ru";b:1;}s:8:"last_url";s:40:"http://www.butik.ru/checkout/cart/index/";s:8:"messages";O:34:"Mage_Core_Model_Message_Collection":2:{s:12:"*_messages";a:0:{}s:20:"*_lastAddedMessage";N;}}customer|a:10:{s:23:"_session_validator_data";a:4:{s:11:"remote_addr";s:13:"195.91.253.98";s:8:"http_via";s:0:"";s:20:"http_x_forwarded_for";s:0:"";s:15:"http_user_agent";s:104:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11";}s:13:"session_hosts";a:1:{s:12:"www.butik.ru";b:1;}s:2:"id";N;s:4:"cart";a:1:{i:567142;a:11:{s:3:"qty";i:1;s:4:"name";s:10:"Брюки";s:5:"price";d:5103;s:3:"url";s:96:"http://www.butik.ru/product/jenskaya-odejda-bryuki-tommy-hilfiger-1m87619322-403-midnight-689207";s:11:"small_image";s:126:"http://www.butik.ru/media/catalog/product/cache/thumbnail/70x90/c96a280f94e22e3ee3823dd0a1a87606/9/8/983840033_2_uploading.jpg";s:17:"manufacturer_name";s:14:"Tommy Hilfiger";s:14:"parent_item_id";N;s:14:"rus_size_value";s:13:"RUS42/180-185";s:16:"brand_size_value";s:4:"4/34";s:14:"int_size_value";N;s:15:"discount_amount";i:0;}}s:11:"summary_qty";d:1;s:15:"total_purchases";N;s:11:"grand_total";d:5103;s:9:"firstname";N;s:8:"lastname";N;s:5:"email";N;}checkout|a:7:{s:23:"_session_validator_data";a:4:{s:11:"remote_addr";s:13:"195.91.253.98";s:8:"http_via";s:0:"";s:20:"http_x_forwarded_for";s:0:"";s:15:"http_user_agent";s:104:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11";}s:13:"session_hosts";a:1:{s:12:"www.butik.ru";b:1;}s:21:"last_added_product_id";s:6:"689207";s:10:"quote_id_1";s:6:"139564";s:16:"cart_was_updated";b:1;s:14:"checkout_state";s:5:"begin";s:8:"messages";O:34:"Mage_Core_Model_Message_Collection":2:{s:12:"*_messages";a:0:{}s:20:"*_lastAddedMessage";O:29:"Mage_Core_Model_Message_Error":6:{s:8:"*_type";s:5:"error";s:8:"*_code";s:84:"Товара 'Брюки' в настоящее время нет в наличии.";s:9:"*_class";s:0:"";s:10:"*_method";s:0:"";s:14:"*_identifier";N;s:12:"*_isSticky";b:0;}}}store_default|a:2:{s:23:"_session_validator_data";a:4:{s:11:"remote_addr";s:13:"195.91.253.98";s:8:"http_via";s:0:"";s:20:"http_x_forwarded_for";s:0:"";s:15:"http_user_agent";s:104:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11";}s:13:"session_hosts";a:1:{s:12:"www.butik.ru";b:1;}}catalog|a:3:{s:23:"_session_validator_data";a:4:{s:11:"remote_addr";s:13:"195.91.253.98";s:8:"http_via";s:0:"";s:20:"http_x_forwarded_for";s:0:"";s:15:"http_user_agent";s:104:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11";}s:13:"session_hosts";a:1:{s:12:"www.butik.ru";b:1;}s:8:"messages";O:34:"Mage_Core_Model_Message_Collection":2:{s:12:"*_messages";a:0:{}s:20:"*_lastAddedMessage";N;}}object|C:10:"TestObject":96:{a:1:{s:4:"item";O:8:"AbcClass":3:{s:1:"a";i:5;s:11:"AbcClassb";s:7:"private";s:4:"*c";i:8;}}} -------------------------------------------------------------------------------- /decoder.go: -------------------------------------------------------------------------------- 1 | package php_session_decoder 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "strings" 7 | 8 | "github.com/yvasiyarov/php_session_decoder/php_serialize" 9 | ) 10 | 11 | type PhpDecoder struct { 12 | source *strings.Reader 13 | decoder *php_serialize.UnSerializer 14 | } 15 | 16 | func NewPhpDecoder(phpSession string) *PhpDecoder { 17 | decoder := &PhpDecoder{ 18 | source: strings.NewReader(phpSession), 19 | decoder: php_serialize.NewUnSerializer(""), 20 | } 21 | decoder.decoder.SetReader(decoder.source) 22 | return decoder 23 | } 24 | 25 | func (self *PhpDecoder) SetSerializedDecodeFunc(f php_serialize.SerializedDecodeFunc) { 26 | self.decoder.SetSerializedDecodeFunc(f) 27 | } 28 | 29 | func (self *PhpDecoder) Decode() (PhpSession, error) { 30 | var ( 31 | name string 32 | err error 33 | value php_serialize.PhpValue 34 | ) 35 | res := make(PhpSession) 36 | 37 | for { 38 | if name, err = self.readName(); err != nil { 39 | break 40 | } 41 | if value, err = self.decoder.Decode(); err != nil { 42 | break 43 | } 44 | res[name] = value 45 | } 46 | 47 | if err == io.EOF { 48 | err = nil 49 | } 50 | return res, err 51 | } 52 | 53 | func (self *PhpDecoder) readName() (string, error) { 54 | var ( 55 | token rune 56 | err error 57 | ) 58 | buf := bytes.NewBuffer([]byte{}) 59 | for { 60 | if token, _, err = self.source.ReadRune(); err != nil || token == SEPARATOR_VALUE_NAME { 61 | break 62 | } else { 63 | buf.WriteRune(token) 64 | } 65 | } 66 | return buf.String(), err 67 | } 68 | -------------------------------------------------------------------------------- /decoder_test.go: -------------------------------------------------------------------------------- 1 | package php_session_decoder 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "testing" 7 | 8 | "github.com/yvasiyarov/php_session_decoder/php_serialize" 9 | ) 10 | 11 | func TestDecodeBooleanValue(t *testing.T) { 12 | decoder := NewPhpDecoder("login_ok|b:1;") 13 | if result, err := decoder.Decode(); err != nil { 14 | t.Errorf("Can not decode boolens value %#v \n", err) 15 | } else { 16 | if v, ok := (result)["login_ok"]; !ok { 17 | t.Errorf("Boolean value was not decoded \n") 18 | } else if v != true { 19 | t.Errorf("Boolean value was incorrectly decoded \n") 20 | } 21 | } 22 | } 23 | 24 | func TestDecodeIntValue(t *testing.T) { 25 | decoder := NewPhpDecoder("inteiro|i:34;") 26 | if result, err := decoder.Decode(); err != nil { 27 | t.Errorf("Can not decode int value %#v \n", err) 28 | } else { 29 | if v, ok := (result)["inteiro"]; !ok { 30 | t.Errorf("Int value was not decoded \n") 31 | } else if v != 34 { 32 | t.Errorf("Int value was decoded incorrectly: %v\n", v) 33 | } 34 | } 35 | } 36 | 37 | func TestDecodeBooleanAndIntValue(t *testing.T) { 38 | decoder := NewPhpDecoder("login_ok|b:1;inteiro|i:34;") 39 | if result, err := decoder.Decode(); err != nil { 40 | t.Errorf("Can not decode int value %#v \n", err) 41 | } else { 42 | if v, ok := (result)["inteiro"]; !ok { 43 | t.Errorf("Int value was not decoded \n") 44 | } else if v != 34 { 45 | t.Errorf("Int value was decoded incorrectly: %v\n", v) 46 | } 47 | } 48 | } 49 | 50 | func TestDecodeFloatValue(t *testing.T) { 51 | decoder := NewPhpDecoder("float_test|d:34.4679999999;") 52 | if result, err := decoder.Decode(); err != nil { 53 | t.Errorf("Can not decode float value %#v \n", err) 54 | } else { 55 | if v, ok := (result)["float_test"]; !ok { 56 | t.Errorf("Float value was not decoded \n") 57 | } else if v != 34.4679999999 { 58 | t.Errorf("Float value was decoded incorrectly: %v\n", v) 59 | } 60 | } 61 | } 62 | 63 | func TestDecodeStringValue(t *testing.T) { 64 | decoder := NewPhpDecoder("name|s:9:\"some text\";") 65 | if result, err := decoder.Decode(); err != nil { 66 | t.Errorf("Can not decode string value %#v \n", err) 67 | } else { 68 | if v, ok := (result)["name"]; !ok { 69 | t.Errorf("String value was not decoded \n") 70 | } else if v != "some text" { 71 | t.Errorf("String value was decoded incorrectly: %v\n", v) 72 | } 73 | } 74 | } 75 | 76 | func TestDecodeArrayValue(t *testing.T) { 77 | decoder := NewPhpDecoder("arr|a:3:{s:4:\"test\";b:1;i:0;i:5;s:5:\"test2\";N;};") 78 | if result, err := decoder.Decode(); err != nil { 79 | t.Errorf("Can not decode array value %#v \n", err) 80 | } else { 81 | if v, ok := (result)["arr"]; !ok { 82 | t.Errorf("Array value was not decoded \n") 83 | } else if arrValue, ok := v.(php_serialize.PhpArray); ok != true { 84 | t.Errorf("Array value was decoded incorrectly: %#v \n", v) 85 | } else if value1, ok := arrValue["test"]; !ok || value1 != true { 86 | t.Errorf("Array value was decoded incorrectly: %#v\n", v) 87 | } else if value2, ok := arrValue[php_serialize.PhpValue(0)]; !ok || value2 != 5 { 88 | t.Errorf("Array value was decoded incorrectly: %#v\n", v) 89 | } else if value3, ok := arrValue["test2"]; !ok || value3 != nil { 90 | t.Errorf("Array value was decoded incorrectly: %#v\n", v) 91 | } 92 | } 93 | } 94 | 95 | func TestDecodeObjectValue(t *testing.T) { 96 | decoder := NewPhpDecoder("obj|O:10:\"TestObject\":3:{s:1:\"a\";i:5;s:13:\"\x00TestObject\x00b\";s:4:\"priv\";s:4:\"\x00*\x00c\";i:8;}") 97 | if result, err := decoder.Decode(); err != nil { 98 | t.Errorf("Can not decode object value %#v \n", err) 99 | } else { 100 | if v, ok := (result)["obj"]; !ok { 101 | t.Errorf("Object value was not decoded \n") 102 | } else if objValue, ok := v.(*php_serialize.PhpObject); ok != true { 103 | t.Errorf("Object value was decoded incorrectly: %#v \n", v) 104 | } else if objValue.GetClassName() != "TestObject" { 105 | t.Errorf("Object name was decoded incorrectly: %#v\n", objValue.GetClassName()) 106 | } else if value1, ok := objValue.GetPublic("a"); !ok || value1 != 5 { 107 | t.Errorf("Public member of object was decoded incorrectly: %#v\n", objValue.GetMembers()) 108 | } else if value2, ok := objValue.GetPrivate("b"); !ok || value2 != "priv" { 109 | t.Errorf("Private member of object was decoded incorrectly: %#v\n", objValue.GetMembers()) 110 | } else if value3, ok := objValue.GetProtected("c"); !ok || value3 != 8 { 111 | t.Errorf("Protected member of object was decoded incorrectly: %#v\n", objValue.GetMembers()) 112 | } 113 | } 114 | } 115 | 116 | func TestDecodeComplexArrayValue(t *testing.T) { 117 | decoder := NewPhpDecoder("arr2|a:6:{s:10:\"bool_false\";b:0;s:7:\"neg_int\";i:-5;s:9:\"neg_float\";d:-5;s:6:\"quotes\";s:22:\"test\" and 'v' and `q` \";s:8:\"not_ansi\";s:8:\"тест\";s:5:\"test3\";s:15:\"@@@ test $$$ \\ \";}") 118 | if result, err := decoder.Decode(); err != nil { 119 | t.Errorf("Can not decode array value %#v \n", err) 120 | } else { 121 | if v, ok := (result)["arr2"]; !ok { 122 | t.Errorf("Array value was not decoded \n") 123 | } else if arrValue, ok := v.(php_serialize.PhpArray); ok != true { 124 | t.Errorf("Array value was decoded incorrectly: %#v \n", v) 125 | } else if value1, ok := arrValue["bool_false"]; !ok || value1 != false { 126 | t.Errorf("Bool false value was decoded incorrectly: %#v\n", v) 127 | } else if value2, ok := arrValue["neg_int"]; !ok || value2 != -5 { 128 | t.Errorf("Negative int value was decoded incorrectly: %#v\n", v) 129 | } else if value3, ok := arrValue["quotes"]; !ok || value3 != "test\" and 'v' and `q` " { 130 | t.Errorf("String with quotes was decoded incorrectly: %#v\n", v) 131 | } else if value4, ok := arrValue["not_ansi"]; !ok || value4 != "тест" { 132 | t.Errorf("String with not ansi symbols was decoded incorrectly: %#v\n", v) 133 | } else if value5, ok := arrValue["test3"]; !ok || value5 != "@@@ test $$$ \\ " { 134 | t.Errorf("String with special symbols was decoded incorrectly: %#v\n", v) 135 | } 136 | } 137 | } 138 | 139 | func TestDecodeMultidimensionalArrayValue(t *testing.T) { 140 | decoder := NewPhpDecoder("arr3|a:1:{s:4:\"dim1\";a:5:{i:0;s:4:\"dim2\";i:1;i:0;i:2;i:3;i:3;i:5;i:4;a:2:{i:0;s:4:\"dim3\";i:1;i:5;}}}") 141 | if result, err := decoder.Decode(); err != nil { 142 | t.Errorf("Can not decode array value %#v \n", err) 143 | } else { 144 | if v, ok := (result)["arr3"]; !ok { 145 | t.Errorf("Array value was not decoded \n") 146 | } else if arrValue, ok := v.(php_serialize.PhpArray); ok != true { 147 | t.Errorf("Array value was decoded incorrectly: %#v \n", v) 148 | } else if dim1, ok := arrValue["dim1"]; !ok { 149 | t.Errorf("Second dimension of array was decoded incorrectly: %#v\n", dim1) 150 | } else if dim1Value, ok := dim1.(php_serialize.PhpArray); ok != true { 151 | t.Errorf("Second dimension of array was decoded incorrectly: %#v\n", dim1Value) 152 | } else if value1, ok := dim1Value[php_serialize.PhpValue(0)]; !ok || value1 != "dim2" { 153 | t.Errorf("Second dimension of array was decoded incorrectly: %#v\n", value1) 154 | } else if value2, ok := dim1Value[php_serialize.PhpValue(3)]; !ok || value2 != 5 { 155 | t.Errorf("Second dimension of array was decoded incorrectly: %#v\n", value2) 156 | } else if dim2, ok := dim1Value[php_serialize.PhpValue(4)]; !ok { 157 | t.Errorf("Third dimension of array was decoded incorrectly: %#v\n", dim2) 158 | } else if dim2Value, ok := dim2.(php_serialize.PhpArray); ok != true { 159 | t.Errorf("Third dimension of array was decoded incorrectly: %#v\n", dim2Value) 160 | } else if value3, ok := dim2Value[php_serialize.PhpValue(0)]; !ok || value3 != "dim3" { 161 | t.Errorf("Third dimension of array was decoded incorrectly: %#v\n", value3) 162 | } 163 | } 164 | } 165 | 166 | func TestDecodeMultipleArraysWithoutSemicolons(t *testing.T) { 167 | decoder := NewPhpDecoder("array1|a:1:{s:5:\"test1\";b:1;}array2|a:1:{s:5:\"test2\";b:1;}") 168 | if result, err := decoder.Decode(); err != nil { 169 | t.Errorf("Can not decode array value %#v \n", err) 170 | } else { 171 | if v, ok := result["array1"]; !ok { 172 | t.Errorf("First array was not decoded \n") 173 | } else if arrValue, ok := v.(php_serialize.PhpArray); ok != true { 174 | t.Errorf("Array value was decoded incorrectly: %#v \n", v) 175 | } else if value1, ok := arrValue["test1"]; !ok || value1 != true { 176 | t.Errorf("Array value was decoded incorrectly: %#v\n", v) 177 | } 178 | 179 | if v, ok := result["array2"]; !ok { 180 | t.Errorf("Second array was not decoded \n") 181 | } else if arrValue, ok := v.(php_serialize.PhpArray); ok != true { 182 | t.Errorf("Array value was decoded incorrectly: %#v \n", v) 183 | } else if value2, ok := arrValue["test2"]; !ok || value2 != true { 184 | t.Errorf("Array value was decoded incorrectly: %#v\n", v) 185 | } 186 | } 187 | } 188 | 189 | func TestDecodeSerializableObjectValueNoFunc(t *testing.T) { 190 | decoder := NewPhpDecoder("obj|C:10:\"TestObject\":49:{a:3:{s:1:\"a\";i:5;s:1:\"b\";s:4:\"priv\";s:1:\"c\";i:8;}}") 191 | if result, err := decoder.Decode(); err != nil { 192 | t.Errorf("Can not decode object value %#v \n", err) 193 | } else { 194 | if v, ok := (result)["obj"]; !ok { 195 | t.Errorf("Object value was not decoded \n") 196 | } else if objValue, ok := v.(*php_serialize.PhpObjectSerialized); ok != true { 197 | t.Errorf("Object value was decoded incorrectly: %#v \n", v) 198 | } else if objValue.GetClassName() != "TestObject" { 199 | t.Errorf("Object name was decoded incorrectly: %#v\n", objValue.GetClassName()) 200 | } else if objValue.GetData() != "a:3:{s:1:\"a\";i:5;s:1:\"b\";s:4:\"priv\";s:1:\"c\";i:8;}" { 201 | t.Errorf("RawData of object was decoded incorrectly: %#v\n", objValue.GetData()) 202 | } 203 | } 204 | } 205 | 206 | func TestDecodeSerializableObjectValue(t *testing.T) { 207 | decoder := NewPhpDecoder("object|C:10:\"TestObject\":96:{a:1:{s:4:\"item\";O:8:\"AbcClass\":3:{s:1:\"a\";i:5;s:11:\"\x00AbcClass\x00b\";s:7:\"private\";s:4:\"\x00*\x00c\";i:8;}}}") 208 | decoder.SetSerializedDecodeFunc(php_serialize.SerializedDecodeFunc(php_serialize.UnSerialize)) 209 | if result, err := decoder.Decode(); err != nil { 210 | t.Errorf("Can not decode object value %#v \n", err) 211 | } else { 212 | if v, ok := (result)["object"]; !ok { 213 | t.Errorf("Object value was not decoded \n") 214 | } else if objValue, ok := v.(*php_serialize.PhpObjectSerialized); ok != true { 215 | t.Errorf("Object value was decoded incorrectly: %#v \n", v) 216 | } else if objValue.GetClassName() != "TestObject" { 217 | t.Errorf("Object name was decoded incorrectly: %#v\n", objValue.GetClassName()) 218 | } else if objValue.GetData() != "a:1:{s:4:\"item\";O:8:\"AbcClass\":3:{s:1:\"a\";i:5;s:11:\"\x00AbcClass\x00b\";s:7:\"private\";s:4:\"\x00*\x00c\";i:8;}}" { 219 | t.Errorf("RawData of object was decoded incorrectly: %#v\n", objValue.GetData()) 220 | } else if vv := objValue.GetValue(); vv == nil { 221 | t.Errorf("Object value decoded incorrectly, expected value as PhpArray, have got: %v\n", objValue.GetValue()) 222 | } else if arrVal, ok := vv.(php_serialize.PhpArray); !ok { 223 | t.Errorf("Unable to convert %v to PhpArray\n", vv) 224 | } else if v1, ok1 := arrVal["item"]; !ok1 { 225 | t.Errorf("Array value decoded incorrectly, key `item` doest not exists\n") 226 | } else if itemObjValue, ok1 := v1.(*php_serialize.PhpObject); !ok1 { 227 | t.Errorf("Unable to convert %v to int\n", v1) 228 | } else if itemObjValue.GetClassName() != "AbcClass" { 229 | t.Errorf("Object name was decoded incorrectly: %#v\n", itemObjValue.GetClassName()) 230 | } else if value1, ok := itemObjValue.GetPublic("a"); !ok || value1 != 5 { 231 | t.Errorf("Public member of object was decoded incorrectly: %#v\n", itemObjValue.GetMembers()) 232 | } else if value2, ok := itemObjValue.GetPrivate("b"); !ok || value2 != "private" { 233 | t.Errorf("Private member of object was decoded incorrectly: %#v\n", itemObjValue.GetMembers()) 234 | } else if value3, ok := itemObjValue.GetProtected("c"); !ok || value3 != 8 { 235 | t.Errorf("Protected member of object was decoded incorrectly: %#v\n", itemObjValue.GetMembers()) 236 | } 237 | } 238 | } 239 | 240 | func TestDecodeSerializableObjectFoo(t *testing.T) { 241 | decoder := NewPhpDecoder("foo|C:3:\"Foo\":3:{foo}") 242 | if result, err := decoder.Decode(); err != nil { 243 | t.Errorf("Can not decode object value %#v \n", err) 244 | } else { 245 | if v, ok := (result)["foo"]; !ok { 246 | t.Errorf("Object value was not decoded \n") 247 | } else if objValue, ok := v.(*php_serialize.PhpObjectSerialized); ok != true { 248 | t.Errorf("Object value was decoded incorrectly: %#v \n", v) 249 | } else if objValue.GetClassName() != "Foo" { 250 | t.Errorf("Object name was decoded incorrectly: %#v\n", objValue.GetClassName()) 251 | } else if objValue.GetData() != "foo" { 252 | t.Errorf("RawData of object was decoded incorrectly: %#v\n", objValue.GetData()) 253 | } 254 | } 255 | } 256 | 257 | func TestDecodeSerializableObjectBar(t *testing.T) { 258 | var f php_serialize.SerializedDecodeFunc 259 | f = func(s string) (php_serialize.PhpValue, error) { 260 | var ( 261 | val map[string]string 262 | err error 263 | ) 264 | err = json.Unmarshal([]byte(s), &val) 265 | return val, err 266 | } 267 | 268 | decoder := NewPhpDecoder("bar|C:3:\"Bar\":19:{{\"public\":\"public\"}}") 269 | decoder.SetSerializedDecodeFunc(f) 270 | if result, err := decoder.Decode(); err != nil { 271 | t.Errorf("Can not decode object value %#v \n", err) 272 | } else { 273 | if v, ok := (result)["bar"]; !ok { 274 | t.Errorf("Object value was not decoded \n") 275 | } else if objValue, ok := v.(*php_serialize.PhpObjectSerialized); ok != true { 276 | t.Errorf("Object value was decoded incorrectly: %#v \n", v) 277 | } else if objValue.GetClassName() != "Bar" { 278 | t.Errorf("Object name was decoded incorrectly: %#v\n", objValue.GetClassName()) 279 | } else if objValue.GetData() != "{\"public\":\"public\"}" { 280 | t.Errorf("RawData of object was decoded incorrectly: %#v\n", objValue.GetData()) 281 | } else if vv := objValue.GetValue(); vv == nil { 282 | t.Errorf("Object value decoded incorrectly, expected value as PhpArray, have got: %v\n", objValue.GetValue()) 283 | } else if arrVal, ok := vv.(map[string]string); !ok { 284 | t.Errorf("Unable to convert %v to map[string]string\n", vv) 285 | } else if v1, ok1 := arrVal["public"]; !ok1 { 286 | t.Errorf("Array value decoded incorrectly, key `public` doest not exists\n") 287 | } else if v1 != "public" { 288 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", "public", v1) 289 | } 290 | } 291 | } 292 | 293 | func TestDecodeRealData(t *testing.T) { 294 | testData, _ := ioutil.ReadFile("./data/test.session") 295 | decoder := NewPhpDecoder(string(testData)) 296 | if result, err := decoder.Decode(); err != nil { 297 | t.Errorf("Can not decode array value %#v \n", err) 298 | } else { 299 | rootKeys := []string{"product_last_viewed", "core", "customer", "checkout", "store_default", "catalog", "object"} 300 | for _, v := range rootKeys { 301 | if _, ok := result[v]; !ok { 302 | t.Errorf("Can not find %v key\n", v) 303 | } 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package php_session_decoder provides possibility to decode/encode php session data in php_binary format. 2 | package php_session_decoder 3 | -------------------------------------------------------------------------------- /encoder.go: -------------------------------------------------------------------------------- 1 | package php_session_decoder 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/yvasiyarov/php_session_decoder/php_serialize" 8 | ) 9 | 10 | type PhpEncoder struct { 11 | data PhpSession 12 | encoder *php_serialize.Serializer 13 | } 14 | 15 | func NewPhpEncoder(data PhpSession) *PhpEncoder { 16 | return &PhpEncoder{ 17 | data: data, 18 | encoder: php_serialize.NewSerializer(), 19 | } 20 | } 21 | 22 | func (self *PhpEncoder) SetSerializedEncodeFunc(f php_serialize.SerializedEncodeFunc) { 23 | self.encoder.SetSerializedEncodeFunc(f) 24 | } 25 | 26 | func (self *PhpEncoder) Encode() (string, error) { 27 | if self.data == nil { 28 | return "", nil 29 | } 30 | var ( 31 | err error 32 | val string 33 | ) 34 | buf := bytes.NewBuffer([]byte{}) 35 | 36 | for k, v := range self.data { 37 | buf.WriteString(k) 38 | buf.WriteRune(SEPARATOR_VALUE_NAME) 39 | if val, err = self.encoder.Encode(v); err != nil { 40 | err = fmt.Errorf("php_session: error during encode value for %q: %v", k, err) 41 | break 42 | } 43 | buf.WriteString(val) 44 | } 45 | 46 | return buf.String(), err 47 | } 48 | -------------------------------------------------------------------------------- /encoder_test.go: -------------------------------------------------------------------------------- 1 | package php_session_decoder 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/yvasiyarov/php_session_decoder/php_serialize" 9 | ) 10 | 11 | func TestEncodeBooleanValue(t *testing.T) { 12 | data := PhpSession{ 13 | "login_ok": true, 14 | } 15 | 16 | encoder := NewPhpEncoder(data) 17 | if result, err := encoder.Encode(); err != nil { 18 | t.Errorf("Can not encode boolens value %#v \n", err) 19 | } else { 20 | if result != "login_ok|b:1;" { 21 | t.Errorf("Boolean value was encoded incorrectly %v \n", result) 22 | } 23 | } 24 | } 25 | 26 | func TestEncodeIntValue(t *testing.T) { 27 | data := PhpSession{ 28 | "inteiro": 34, 29 | } 30 | 31 | encoder := NewPhpEncoder(data) 32 | if result, err := encoder.Encode(); err != nil { 33 | t.Errorf("Can not encode int value %#v \n", err) 34 | } else { 35 | if result != "inteiro|i:34;" { 36 | t.Errorf("Int value was encoded incorrectly %v \n", result) 37 | } 38 | } 39 | } 40 | 41 | func TestEncodeFloatValue(t *testing.T) { 42 | data := PhpSession{ 43 | "float_test": 34.4679999999, 44 | } 45 | 46 | encoder := NewPhpEncoder(data) 47 | if result, err := encoder.Encode(); err != nil { 48 | t.Errorf("Can not encode float value %#v \n", err) 49 | } else { 50 | // 34.467999999900002 - PHP has precision = 17 by default 51 | if result != "float_test|d:34.467999999900002;" { 52 | t.Errorf("Float value was encoded incorrectly %v\n", result) 53 | } 54 | } 55 | } 56 | 57 | func TestEncodeStringValue(t *testing.T) { 58 | data := PhpSession{ 59 | "name": "some text", 60 | } 61 | 62 | encoder := NewPhpEncoder(data) 63 | if result, err := encoder.Encode(); err != nil { 64 | t.Errorf("Can not encode string value %#v \n", err) 65 | } else { 66 | if result != "name|s:9:\"some text\";" { 67 | t.Errorf("String value was encoded incorrectly %v\n", result) 68 | } 69 | } 70 | } 71 | 72 | func TestEncodeArrayValue(t *testing.T) { 73 | data := PhpSession{ 74 | "arr": php_serialize.PhpArray{ 75 | // Zero element 76 | //php_serialize.PhpValue(0): 5, 77 | 0: 5, 78 | "test": true, 79 | "test2": nil, 80 | }, 81 | } 82 | 83 | encoder := NewPhpEncoder(data) 84 | if result, err := encoder.Encode(); err != nil { 85 | t.Errorf("Can not encode array value %#v \n", err) 86 | } else { 87 | if !strings.Contains(result, "i:0;i:5;") || !strings.Contains(result, "s:4:\"test\";b:1") || !strings.Contains(result, "s:5:\"test2\";N") { 88 | t.Errorf("Array value was encoded incorrectly %v\n", result) 89 | } 90 | } 91 | } 92 | 93 | func TestEncodeObjectValue(t *testing.T) { 94 | obj := php_serialize.NewPhpObject("TestObject") 95 | obj.SetPublic("a", 5) 96 | obj.SetProtected("c", 8) 97 | obj.SetPrivate("b", "priv") 98 | data := PhpSession{ 99 | "obj": obj, 100 | } 101 | 102 | encoder := NewPhpEncoder(data) 103 | if result, err := encoder.Encode(); err != nil { 104 | t.Errorf("Can not encode object value %#v \n", err) 105 | } else { 106 | if !strings.Contains(result, "s:1:\"a\";i:5") || !strings.Contains(result, "10:\"TestObject\"") || !strings.Contains(result, "s:13:\"\x00TestObject\x00b\";s:4:\"priv\"") || !strings.Contains(result, "s:4:\"\x00*\x00c\";i:8") { 107 | t.Errorf("Object value was encoded incorrectly %v\n", result) 108 | } 109 | } 110 | } 111 | 112 | func TestEncodeSerializableObjectValueNoFunc(t *testing.T) { 113 | obj := php_serialize.NewPhpObjectSerialized("TestObject") 114 | obj.SetData("a:3:{s:1:\"a\";i:5;s:1:\"b\";s:4:\"priv\";s:1:\"c\";i:8;}") 115 | data := PhpSession{ 116 | "obj": obj, 117 | } 118 | 119 | encoder := NewPhpEncoder(data) 120 | if result, err := encoder.Encode(); err != nil { 121 | t.Errorf("Can not encode object value %#v \n", err) 122 | } else { 123 | if !strings.Contains(result, "a:3:{s:1:\"a\";i:5;s:1:\"b\";s:4:\"priv\";s:1:\"c\";i:8;}") || !strings.Contains(result, "C:10:\"TestObject\"") { 124 | t.Errorf("Object value was encoded incorrectly %v\n", result) 125 | } 126 | } 127 | } 128 | 129 | func TestEncodeSerializableObjectValue(t *testing.T) { 130 | arr := php_serialize.PhpArray{ 131 | "a": 5, 132 | "b": "priv", 133 | "c": 8, 134 | } 135 | obj := php_serialize.NewPhpObjectSerialized("TestObject") 136 | obj.SetValue(php_serialize.PhpValue(arr)) 137 | data := PhpSession{ 138 | "obj": obj, 139 | } 140 | 141 | encoder := NewPhpEncoder(data) 142 | encoder.SetSerializedEncodeFunc(php_serialize.SerializedEncodeFunc(php_serialize.Serialize)) 143 | if result, err := encoder.Encode(); err != nil { 144 | t.Errorf("Can not encode object value %#v \n", err) 145 | } else { 146 | if !strings.Contains(result, "C:10:\"TestObject\"") { 147 | t.Errorf("Object value was encoded incorrectly %v\n", result) 148 | } else if !strings.Contains(result, "s:1:\"a\";i:5;") { 149 | t.Errorf("Object value was encoded incorrectly %v\n", result) 150 | } else if !strings.Contains(result, "s:1:\"b\";s:4:\"priv\";") { 151 | t.Errorf("Object value was encoded incorrectly %v\n", result) 152 | } else if !strings.Contains(result, "s:1:\"c\";i:8;") { 153 | t.Errorf("Object value was encoded incorrectly %v\n", result) 154 | } 155 | } 156 | } 157 | 158 | func TestEncodeSerializableObjectValueJSON(t *testing.T) { 159 | var f php_serialize.SerializedEncodeFunc 160 | f = func(v php_serialize.PhpValue) (string, error) { 161 | res, err := json.Marshal(v) 162 | return string(res), err 163 | } 164 | 165 | obj := php_serialize.NewPhpObjectSerialized("Bar") 166 | obj.SetValue(map[string]string{"public": "public"}) 167 | data := PhpSession{ 168 | "bar": obj, 169 | } 170 | 171 | encoder := NewPhpEncoder(data) 172 | encoder.SetSerializedEncodeFunc(f) 173 | if result, err := encoder.Encode(); err != nil { 174 | t.Errorf("Can not encode object value %#v \n", err) 175 | } else { 176 | if result != "bar|C:3:\"Bar\":19:{{\"public\":\"public\"}}" { 177 | t.Errorf("Object value was encoded incorrectly %v\n", result) 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package php_session_decoder 4 | 5 | func Fuzz(data []byte) int { 6 | decoder := NewPhpDecoder(string(data)) 7 | _, err := decoder.Decode() 8 | 9 | if err != nil { 10 | return 0 11 | } 12 | 13 | return 1 14 | } 15 | -------------------------------------------------------------------------------- /fuzz_test.go: -------------------------------------------------------------------------------- 1 | package php_session_decoder 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFuzzCrashers(t *testing.T) { 8 | 9 | var crashers = []string{ 10 | "|C2984619140625:", 11 | "|C9478759765625:", 12 | "|C :590791705756156:", 13 | "|C298461940625:", 14 | } 15 | 16 | for _, f := range crashers { 17 | decoder := NewPhpDecoder(f) 18 | decoder.Decode() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /nut.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "0.1.1", 3 | "Vendor": "yvasiyarov", 4 | "Authors": [ 5 | { 6 | "FullName": "Yuriy Vasiyarov", 7 | "Email": "varyous@gmail.com" 8 | } 9 | ], 10 | "ExtraFiles": [ 11 | "README.md", 12 | "LICENSE" 13 | ], 14 | "Homepage": "https://github.com/yvasiyarov/php_session_decoder" 15 | } 16 | -------------------------------------------------------------------------------- /php_serialize/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /php_serialize/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /php_serialize/README.md: -------------------------------------------------------------------------------- 1 | PHP Serialize/Unserialize in Go/GoLang 2 | =================== 3 | 4 | This is the simple implementation of PHP `serialize` and `unserialize` functions written in Go/GoLang. 5 | This package was inspired by [@yvasiyarov](https://github.com/yvasiyarov) and improved by [@fromYukki](https://github.com/fromYukki). 6 | Feel free to use it as you wish ;) 7 | 8 | UnSerialize 9 | --------------- 10 | 11 | decoder := NewUnSerializer("Some serialized string") 12 | if val, err := decoder.Decode(); err != nil { 13 | panic(err) 14 | } else { 15 | // val - is your PhpValue instance 16 | } 17 | 18 | Some details: 19 | 20 | * Any of PHP variable will be decoded as `PhpValue` type, and you need to cast it at your own type (int, string etc..); 21 | * Any integer may be converted to `int` (I'm sure that you know about 32 or 64 bits); 22 | * Any decimal my be converted to `float64`; 23 | * Any PHP arrays will be decoded as `PhpArray` type. This is the map of `PhpValue` All keys and values are `PhpValue`; 24 | * Any PHP objects will be decoded as `PhpObject`; 25 | * Any PHP objects that implement a `Serializable` interface wil be decoded as `PhpObjectSerialized`. Please remember it is not the same as `PhpObject`; 26 | * You can set your own unserialize function for objects that implement a `Serializable` interface by using `SetSerializedDecodeFunc` function. 27 | 28 | Serialize 29 | --------------- 30 | 31 | encoder := NewSerializer() 32 | if val, err := encoder.Encode(source); err != nil { 33 | panic(err) 34 | } else { 35 | // val - is your serialized string 36 | } 37 | 38 | Encode function expects `PhpValue` variable as argument. 39 | 40 | TODO: 41 | --------------- 42 | 43 | * Write more informative README and some useful examples 44 | -------------------------------------------------------------------------------- /php_serialize/common.go: -------------------------------------------------------------------------------- 1 | package php_serialize 2 | 3 | const ( 4 | TOKEN_NULL rune = 'N' 5 | TOKEN_BOOL rune = 'b' 6 | TOKEN_INT rune = 'i' 7 | TOKEN_FLOAT rune = 'd' 8 | TOKEN_STRING rune = 's' 9 | TOKEN_ARRAY rune = 'a' 10 | TOKEN_OBJECT rune = 'O' 11 | TOKEN_OBJECT_SERIALIZED rune = 'C' 12 | TOKEN_REFERENCE rune = 'R' 13 | TOKEN_REFERENCE_OBJECT rune = 'r' 14 | TOKEN_SPL_ARRAY rune = 'x' 15 | TOKEN_SPL_ARRAY_MEMBERS rune = 'm' 16 | 17 | SEPARATOR_VALUE_TYPE rune = ':' 18 | SEPARATOR_VALUES rune = ';' 19 | 20 | DELIMITER_STRING_LEFT rune = '"' 21 | DELIMITER_STRING_RIGHT rune = '"' 22 | DELIMITER_OBJECT_LEFT rune = '{' 23 | DELIMITER_OBJECT_RIGHT rune = '}' 24 | 25 | FORMATTER_FLOAT byte = 'g' 26 | FORMATTER_PRECISION int = 17 27 | ) 28 | 29 | var ( 30 | debugMode = false 31 | ) 32 | 33 | func Debug(value bool) { 34 | debugMode = value 35 | } 36 | 37 | func NewPhpObject(className string) *PhpObject { 38 | return &PhpObject{ 39 | className: className, 40 | members: PhpArray{}, 41 | } 42 | } 43 | 44 | type SerializedDecodeFunc func(string) (PhpValue, error) 45 | 46 | type SerializedEncodeFunc func(PhpValue) (string, error) 47 | 48 | type PhpValue interface{} 49 | 50 | type PhpArray map[PhpValue]PhpValue 51 | 52 | type PhpSlice []PhpValue 53 | 54 | type PhpObject struct { 55 | className string 56 | members PhpArray 57 | } 58 | 59 | func (self *PhpObject) GetClassName() string { 60 | return self.className 61 | } 62 | 63 | func (self *PhpObject) SetClassName(name string) *PhpObject { 64 | self.className = name 65 | return self 66 | } 67 | 68 | func (self *PhpObject) GetMembers() PhpArray { 69 | return self.members 70 | } 71 | 72 | func (self *PhpObject) SetMembers(members PhpArray) *PhpObject { 73 | self.members = members 74 | return self 75 | } 76 | 77 | func (self *PhpObject) GetPrivate(name string) (v PhpValue, ok bool) { 78 | v, ok = self.members["\x00"+self.className+"\x00"+name] 79 | return 80 | } 81 | 82 | func (self *PhpObject) SetPrivate(name string, value PhpValue) *PhpObject { 83 | self.members["\x00"+self.className+"\x00"+name] = value 84 | return self 85 | } 86 | 87 | func (self *PhpObject) GetProtected(name string) (v PhpValue, ok bool) { 88 | v, ok = self.members["\x00*\x00"+name] 89 | return 90 | } 91 | 92 | func (self *PhpObject) SetProtected(name string, value PhpValue) *PhpObject { 93 | self.members["\x00*\x00"+name] = value 94 | return self 95 | } 96 | 97 | func (self *PhpObject) GetPublic(name string) (v PhpValue, ok bool) { 98 | v, ok = self.members[name] 99 | return 100 | } 101 | 102 | func (self *PhpObject) SetPublic(name string, value PhpValue) *PhpObject { 103 | self.members[name] = value 104 | return self 105 | } 106 | 107 | func NewPhpObjectSerialized(className string) *PhpObjectSerialized { 108 | return &PhpObjectSerialized{ 109 | className: className, 110 | } 111 | } 112 | 113 | type PhpObjectSerialized struct { 114 | className string 115 | data string 116 | value PhpValue 117 | } 118 | 119 | func (self *PhpObjectSerialized) GetClassName() string { 120 | return self.className 121 | } 122 | 123 | func (self *PhpObjectSerialized) SetClassName(name string) *PhpObjectSerialized { 124 | self.className = name 125 | return self 126 | } 127 | 128 | func (self *PhpObjectSerialized) GetData() string { 129 | return self.data 130 | } 131 | 132 | func (self *PhpObjectSerialized) SetData(data string) *PhpObjectSerialized { 133 | self.data = data 134 | return self 135 | } 136 | 137 | func (self *PhpObjectSerialized) GetValue() PhpValue { 138 | return self.value 139 | } 140 | 141 | func (self *PhpObjectSerialized) SetValue(value PhpValue) *PhpObjectSerialized { 142 | self.value = value 143 | return self 144 | } 145 | 146 | func NewPhpSplArray(array, properties PhpValue) *PhpSplArray { 147 | if array == nil { 148 | array = make(PhpArray) 149 | } 150 | 151 | if properties == nil { 152 | properties = make(PhpArray) 153 | } 154 | 155 | return &PhpSplArray{ 156 | array: array, 157 | properties: properties, 158 | } 159 | } 160 | 161 | type PhpSplArray struct { 162 | flags int 163 | array PhpValue 164 | properties PhpValue 165 | } 166 | 167 | func (self *PhpSplArray) GetFlags() int { 168 | return self.flags 169 | } 170 | 171 | func (self *PhpSplArray) SetFlags(value int) { 172 | self.flags = value 173 | } 174 | 175 | func (self *PhpSplArray) GetArray() PhpValue { 176 | return self.array 177 | } 178 | 179 | func (self *PhpSplArray) SetArray(value PhpValue) { 180 | self.array = value 181 | } 182 | 183 | func (self *PhpSplArray) GetProperties() PhpValue { 184 | return self.properties 185 | } 186 | 187 | func (self *PhpSplArray) SetProperties(value PhpValue) { 188 | self.properties = value 189 | } 190 | -------------------------------------------------------------------------------- /php_serialize/common_helper.go: -------------------------------------------------------------------------------- 1 | package php_serialize 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | func PhpValueString(p PhpValue) (res string) { 8 | res, _ = p.(string) 9 | return 10 | } 11 | 12 | func PhpValueBool(p PhpValue) (res bool) { 13 | switch p.(type) { 14 | case bool: 15 | res, _ = p.(bool) 16 | case string: 17 | str, _ := p.(string) 18 | res, _ = strconv.ParseBool(str) 19 | } 20 | return 21 | } 22 | 23 | func PhpValueInt(p PhpValue) (res int) { 24 | switch p.(type) { 25 | case int: 26 | res, _ = p.(int) 27 | case int8: 28 | intVal, _ := p.(int8) 29 | res = int(intVal) 30 | case int16: 31 | intVal, _ := p.(int16) 32 | res = int(intVal) 33 | case int32: 34 | intVal, _ := p.(int32) 35 | res = int(intVal) 36 | case int64: 37 | intVal, _ := p.(int64) 38 | res = int(intVal) 39 | case uint: 40 | intVal, _ := p.(uint) 41 | res = int(intVal) 42 | case uint8: 43 | intVal, _ := p.(uint8) 44 | res = int(intVal) 45 | case uint16: 46 | intVal, _ := p.(uint16) 47 | res = int(intVal) 48 | case uint32: 49 | intVal, _ := p.(uint32) 50 | res = int(intVal) 51 | case uint64: 52 | intVal, _ := p.(uint64) 53 | res = int(intVal) 54 | case string: 55 | str, _ := p.(string) 56 | res, _ = strconv.Atoi(str) 57 | } 58 | return 59 | } 60 | 61 | func PhpValueInt64(p PhpValue) (res int64) { 62 | switch p.(type) { 63 | case int64: 64 | res = p.(int64) 65 | default: 66 | res = int64(PhpValueInt(p)) 67 | } 68 | return 69 | } 70 | 71 | func PhpValueUInt(p PhpValue) (res uint) { 72 | switch p.(type) { 73 | case uint: 74 | res = p.(uint) 75 | default: 76 | res = uint(PhpValueInt(p)) 77 | } 78 | return 79 | } 80 | 81 | func PhpValueUInt64(p PhpValue) (res uint64) { 82 | switch p.(type) { 83 | case uint64: 84 | res = p.(uint64) 85 | default: 86 | res = uint64(PhpValueInt(p)) 87 | } 88 | return 89 | } 90 | 91 | func PhpValueFloat64(p PhpValue) (res float64) { 92 | switch p.(type) { 93 | case float64: 94 | res, _ = p.(float64) 95 | case string: 96 | str, _ := p.(string) 97 | res, _ = strconv.ParseFloat(str, 64) 98 | default: 99 | return float64(PhpValueInt(p)) 100 | } 101 | return 102 | } 103 | -------------------------------------------------------------------------------- /php_serialize/common_helper_test.go: -------------------------------------------------------------------------------- 1 | package php_serialize 2 | 3 | import "testing" 4 | 5 | func TestPhpValueString(t *testing.T) { 6 | var ( 7 | val PhpValue = "string" 8 | expected string = "string" 9 | ) 10 | if newVal := PhpValueString(val); newVal != expected { 11 | t.Errorf("Expected %q but got %q", expected, newVal) 12 | } 13 | } 14 | 15 | func TestPhpValueBool(t *testing.T) { 16 | var ( 17 | val PhpValue = true 18 | expected bool = true 19 | ) 20 | if newVal := PhpValueBool(val); newVal != expected { 21 | t.Errorf("Expected %t but got %t", expected, newVal) 22 | } 23 | } 24 | 25 | func TestPhpValueInt(t *testing.T) { 26 | var ( 27 | val PhpValue = 10 28 | expected int = 10 29 | ) 30 | if newVal := PhpValueInt(val); newVal != expected { 31 | t.Errorf("Expected %d but got %d", expected, newVal) 32 | } 33 | } 34 | 35 | func TestPhpValueInt64(t *testing.T) { 36 | var ( 37 | val PhpValue = int64(10) 38 | expected int64 = 10 39 | ) 40 | if newVal := PhpValueInt64(val); newVal != expected { 41 | t.Errorf("Expected %d but got %d", expected, newVal) 42 | } 43 | } 44 | 45 | func TestPhpValueUInt(t *testing.T) { 46 | var ( 47 | val PhpValue = uint(10) 48 | expected uint = 10 49 | ) 50 | if newVal := PhpValueUInt(val); newVal != expected { 51 | t.Errorf("Expected %d but got %d", expected, newVal) 52 | } 53 | } 54 | 55 | func TestPhpValueUInt64(t *testing.T) { 56 | var ( 57 | val PhpValue = uint64(10) 58 | expected uint64 = 10 59 | ) 60 | if newVal := PhpValueUInt64(val); newVal != expected { 61 | t.Errorf("Expected %d but got %d", expected, newVal) 62 | } 63 | } 64 | 65 | func TestPhpValueFloat64(t *testing.T) { 66 | var ( 67 | val PhpValue = float64(10.0) 68 | expected float64 = 10.0 69 | ) 70 | if newVal := PhpValueFloat64(val); newVal != expected { 71 | t.Errorf("Expected %v but got %v", expected, newVal) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /php_serialize/php/test.php: -------------------------------------------------------------------------------- 1 | $this->foo, 'bar' => $this->bar)); 36 | } 37 | public function unserialize($str) { 38 | // ... 39 | } 40 | } 41 | 42 | class TestSerializable2 implements Serializable { 43 | public $foo = 4; 44 | public $bar = 2; 45 | 46 | public function serialize() { 47 | return json_encode(array('foo' => $this->foo, 'bar' => $this->bar)); 48 | } 49 | public function unserialize($str) { 50 | // ... 51 | } 52 | } 53 | 54 | echo '===============' . PHP_EOL; 55 | 56 | serializeMe('Nul', null); 57 | serializeMe('Bool True', true); 58 | serializeMe('Bool False', false); 59 | serializeMe('Int', 42); 60 | serializeMe('Int Minus', -42); 61 | serializeMe('Float', 42.3789); 62 | serializeMe('Float Minus', -42.3789); 63 | serializeMe('String', 'foobar'); 64 | serializeMe('Array', array(10, 11, 12)); 65 | serializeMe('Array Keys', array('foo' => 4, 'bar' => 2)); 66 | serializeMe('Array Array', array('foo' => array(10, 11, 12), 'bar' => 2)); 67 | serializeMe('Object', new Test()); 68 | serializeMe('Array Object', array(new Test1(), new Test2())); 69 | serializeMe('Serializable Empty Object', new TestSerializable()); 70 | serializeMe('Serializable Array Object', new TestSerializable1()); 71 | serializeMe('Serializable JSON Object', new TestSerializable2()); 72 | 73 | echo '===============' . PHP_EOL; 74 | 75 | function serializeMe($message, $object) { 76 | echo $message . PHP_EOL; 77 | echo json_encode(serialize($object)) . PHP_EOL . PHP_EOL; 78 | } -------------------------------------------------------------------------------- /php_serialize/serialize.go: -------------------------------------------------------------------------------- 1 | package php_serialize 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | func Serialize(v PhpValue) (string, error) { 10 | encoder := NewSerializer() 11 | encoder.SetSerializedEncodeFunc(SerializedEncodeFunc(Serialize)) 12 | return encoder.Encode(v) 13 | } 14 | 15 | type Serializer struct { 16 | lastErr error 17 | encodeFunc SerializedEncodeFunc 18 | } 19 | 20 | func NewSerializer() *Serializer { 21 | return &Serializer{} 22 | } 23 | 24 | func (self *Serializer) SetSerializedEncodeFunc(f SerializedEncodeFunc) { 25 | self.encodeFunc = f 26 | } 27 | 28 | func (self *Serializer) Encode(v PhpValue) (string, error) { 29 | var value bytes.Buffer 30 | 31 | switch t := v.(type) { 32 | default: 33 | self.saveError(fmt.Errorf("php_serialize: Unknown type %T with value %#v", t, v)) 34 | case nil: 35 | value = self.encodeNull() 36 | case bool: 37 | value = self.encodeBool(v) 38 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: 39 | value = self.encodeNumber(v) 40 | case string: 41 | value = self.encodeString(v, DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, true) 42 | case PhpArray, map[PhpValue]PhpValue, PhpSlice: 43 | value = self.encodeArray(v, true) 44 | case *PhpObject: 45 | value = self.encodeObject(v) 46 | case *PhpObjectSerialized: 47 | value = self.encodeSerialized(v) 48 | case *PhpSplArray: 49 | value = self.encodeSplArray(v) 50 | } 51 | 52 | return value.String(), self.lastErr 53 | } 54 | 55 | func (self *Serializer) encodeNull() (buffer bytes.Buffer) { 56 | buffer.WriteRune(TOKEN_NULL) 57 | buffer.WriteRune(SEPARATOR_VALUES) 58 | return 59 | } 60 | 61 | func (self *Serializer) encodeBool(v PhpValue) (buffer bytes.Buffer) { 62 | buffer.WriteRune(TOKEN_BOOL) 63 | buffer.WriteRune(SEPARATOR_VALUE_TYPE) 64 | 65 | if bVal, ok := v.(bool); ok && bVal == true { 66 | buffer.WriteString("1") 67 | } else { 68 | buffer.WriteString("0") 69 | } 70 | 71 | buffer.WriteRune(SEPARATOR_VALUES) 72 | return 73 | } 74 | 75 | func (self *Serializer) encodeNumber(v PhpValue) (buffer bytes.Buffer) { 76 | var val string 77 | 78 | isFloat := false 79 | 80 | switch v.(type) { 81 | default: 82 | val = "0" 83 | case int: 84 | intVal, _ := v.(int) 85 | val = strconv.FormatInt(int64(intVal), 10) 86 | case int8: 87 | intVal, _ := v.(int8) 88 | val = strconv.FormatInt(int64(intVal), 10) 89 | case int16: 90 | intVal, _ := v.(int16) 91 | val = strconv.FormatInt(int64(intVal), 10) 92 | case int32: 93 | intVal, _ := v.(int32) 94 | val = strconv.FormatInt(int64(intVal), 10) 95 | case int64: 96 | intVal, _ := v.(int64) 97 | val = strconv.FormatInt(int64(intVal), 10) 98 | case uint: 99 | intVal, _ := v.(uint) 100 | val = strconv.FormatUint(uint64(intVal), 10) 101 | case uint8: 102 | intVal, _ := v.(uint8) 103 | val = strconv.FormatUint(uint64(intVal), 10) 104 | case uint16: 105 | intVal, _ := v.(uint16) 106 | val = strconv.FormatUint(uint64(intVal), 10) 107 | case uint32: 108 | intVal, _ := v.(uint32) 109 | val = strconv.FormatUint(uint64(intVal), 10) 110 | case uint64: 111 | intVal, _ := v.(uint64) 112 | val = strconv.FormatUint(uint64(intVal), 10) 113 | // PHP has precision = 17 by default 114 | case float32: 115 | floatVal, _ := v.(float32) 116 | val = strconv.FormatFloat(float64(floatVal), FORMATTER_FLOAT, FORMATTER_PRECISION, 32) 117 | isFloat = true 118 | case float64: 119 | floatVal, _ := v.(float64) 120 | val = strconv.FormatFloat(float64(floatVal), FORMATTER_FLOAT, FORMATTER_PRECISION, 64) 121 | isFloat = true 122 | } 123 | 124 | if isFloat { 125 | buffer.WriteRune(TOKEN_FLOAT) 126 | } else { 127 | buffer.WriteRune(TOKEN_INT) 128 | } 129 | 130 | buffer.WriteRune(SEPARATOR_VALUE_TYPE) 131 | buffer.WriteString(val) 132 | buffer.WriteRune(SEPARATOR_VALUES) 133 | 134 | return 135 | } 136 | 137 | func (self *Serializer) encodeString(v PhpValue, left, right rune, isFinal bool) (buffer bytes.Buffer) { 138 | val, _ := v.(string) 139 | 140 | if isFinal { 141 | buffer.WriteRune(TOKEN_STRING) 142 | } 143 | 144 | buffer.WriteString(self.prepareLen(len(val))) 145 | buffer.WriteRune(left) 146 | buffer.WriteString(val) 147 | buffer.WriteRune(right) 148 | 149 | if isFinal { 150 | buffer.WriteRune(SEPARATOR_VALUES) 151 | } 152 | 153 | return 154 | } 155 | 156 | func (self *Serializer) encodeArray(v PhpValue, isFinal bool) (buffer bytes.Buffer) { 157 | var ( 158 | arrLen int 159 | s string 160 | ) 161 | 162 | if isFinal { 163 | buffer.WriteRune(TOKEN_ARRAY) 164 | } 165 | 166 | switch v.(type) { 167 | case PhpArray: 168 | arrVal, _ := v.(PhpArray) 169 | arrLen = len(arrVal) 170 | 171 | buffer.WriteString(self.prepareLen(arrLen)) 172 | buffer.WriteRune(DELIMITER_OBJECT_LEFT) 173 | 174 | for k, v := range arrVal { 175 | s, _ = self.Encode(k) 176 | buffer.WriteString(s) 177 | s, _ = self.Encode(v) 178 | buffer.WriteString(s) 179 | } 180 | 181 | case map[PhpValue]PhpValue: 182 | arrVal, _ := v.(map[PhpValue]PhpValue) 183 | arrLen = len(arrVal) 184 | 185 | buffer.WriteString(self.prepareLen(arrLen)) 186 | buffer.WriteRune(DELIMITER_OBJECT_LEFT) 187 | 188 | for k, v := range arrVal { 189 | s, _ = self.Encode(k) 190 | buffer.WriteString(s) 191 | s, _ = self.Encode(v) 192 | buffer.WriteString(s) 193 | } 194 | case PhpSlice: 195 | arrVal, _ := v.(PhpSlice) 196 | arrLen = len(arrVal) 197 | 198 | buffer.WriteString(self.prepareLen(arrLen)) 199 | buffer.WriteRune(DELIMITER_OBJECT_LEFT) 200 | 201 | for k, v := range arrVal { 202 | s, _ = self.Encode(k) 203 | buffer.WriteString(s) 204 | s, _ = self.Encode(v) 205 | buffer.WriteString(s) 206 | } 207 | } 208 | 209 | buffer.WriteRune(DELIMITER_OBJECT_RIGHT) 210 | 211 | return 212 | } 213 | 214 | func (self *Serializer) encodeObject(v PhpValue) (buffer bytes.Buffer) { 215 | obj, _ := v.(*PhpObject) 216 | buffer.WriteRune(TOKEN_OBJECT) 217 | buffer.WriteString(self.prepareClassName(obj.className)) 218 | encoded := self.encodeArray(obj.members, false) 219 | buffer.WriteString(encoded.String()) 220 | return 221 | } 222 | 223 | func (self *Serializer) encodeSerialized(v PhpValue) (buffer bytes.Buffer) { 224 | var serialized string 225 | 226 | obj, _ := v.(*PhpObjectSerialized) 227 | buffer.WriteRune(TOKEN_OBJECT_SERIALIZED) 228 | buffer.WriteString(self.prepareClassName(obj.className)) 229 | 230 | if self.encodeFunc == nil { 231 | serialized = obj.GetData() 232 | } else { 233 | var err error 234 | if serialized, err = self.encodeFunc(obj.GetValue()); err != nil { 235 | self.saveError(err) 236 | } 237 | } 238 | 239 | encoded := self.encodeString(serialized, DELIMITER_OBJECT_LEFT, DELIMITER_OBJECT_RIGHT, false) 240 | buffer.WriteString(encoded.String()) 241 | return 242 | } 243 | 244 | func (self *Serializer) encodeSplArray(v PhpValue) bytes.Buffer { 245 | var buffer bytes.Buffer 246 | obj, _ := v.(*PhpSplArray) 247 | 248 | buffer.WriteRune(TOKEN_SPL_ARRAY) 249 | buffer.WriteRune(SEPARATOR_VALUE_TYPE) 250 | 251 | encoded := self.encodeNumber(obj.flags) 252 | buffer.WriteString(encoded.String()) 253 | 254 | data, _ := self.Encode(obj.array) 255 | buffer.WriteString(data) 256 | 257 | buffer.WriteRune(SEPARATOR_VALUES) 258 | buffer.WriteRune(TOKEN_SPL_ARRAY_MEMBERS) 259 | buffer.WriteRune(SEPARATOR_VALUE_TYPE) 260 | 261 | data, _ = self.Encode(obj.properties) 262 | buffer.WriteString(data) 263 | 264 | return buffer 265 | } 266 | 267 | func (self *Serializer) prepareLen(l int) string { 268 | return string(SEPARATOR_VALUE_TYPE) + strconv.Itoa(l) + string(SEPARATOR_VALUE_TYPE) 269 | } 270 | 271 | func (self *Serializer) prepareClassName(name string) string { 272 | encoded := self.encodeString(name, DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, false) 273 | return encoded.String() 274 | } 275 | 276 | func (self *Serializer) saveError(err error) { 277 | if self.lastErr == nil { 278 | self.lastErr = err 279 | } 280 | } 281 | 282 | func wrapWithRune(s string, left, right rune) string { 283 | return string(left) + s + string(right) 284 | } 285 | -------------------------------------------------------------------------------- /php_serialize/serialize_test.go: -------------------------------------------------------------------------------- 1 | package php_serialize 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestEncodeNil(t *testing.T) { 10 | var ( 11 | source PhpValue 12 | val string 13 | err error 14 | ) 15 | 16 | source = nil 17 | encoder := NewSerializer() 18 | if val, err = encoder.Encode(source); err != nil { 19 | t.Errorf("Error while encoding nil value: %v\n", err) 20 | } else { 21 | if val != "N;" { 22 | t.Errorf("Nil value decoded incorrectly, have got %q\n", val) 23 | } 24 | } 25 | } 26 | 27 | func TestEncodeBoolTrue(t *testing.T) { 28 | var ( 29 | source PhpValue 30 | val string 31 | err error 32 | ) 33 | 34 | source = true 35 | encoder := NewSerializer() 36 | if val, err = encoder.Encode(source); err != nil { 37 | t.Errorf("Error while encoding bool value: %v\n", err) 38 | } else { 39 | if val != "b:1;" { 40 | t.Errorf("Bool value decoded incorrectly, have got %q\n", val) 41 | } 42 | } 43 | } 44 | 45 | func TestEncodeBoolFalse(t *testing.T) { 46 | var ( 47 | source PhpValue 48 | val string 49 | err error 50 | ) 51 | 52 | source = false 53 | encoder := NewSerializer() 54 | if val, err = encoder.Encode(source); err != nil { 55 | t.Errorf("Error while encoding bool value: %v\n", err) 56 | } else { 57 | if val != "b:0;" { 58 | t.Errorf("Bool value decoded incorrectly, have got %q\n", val) 59 | } 60 | } 61 | } 62 | 63 | func TestEncodeInt(t *testing.T) { 64 | var ( 65 | source PhpValue 66 | val string 67 | err error 68 | ) 69 | 70 | source = 42 71 | encoder := NewSerializer() 72 | if val, err = encoder.Encode(source); err != nil { 73 | t.Errorf("Error while encoding int value: %v\n", err) 74 | } else { 75 | if val != "i:42;" { 76 | t.Errorf("Int value decoded incorrectly, have got %q\n", val) 77 | } 78 | } 79 | } 80 | 81 | func TestEncodeIntMinus(t *testing.T) { 82 | var ( 83 | source PhpValue 84 | val string 85 | err error 86 | ) 87 | 88 | source = -42 89 | encoder := NewSerializer() 90 | if val, err = encoder.Encode(source); err != nil { 91 | t.Errorf("Error while encoding int value: %v\n", err) 92 | } else { 93 | if val != "i:-42;" { 94 | t.Errorf("Int value decoded incorrectly, have got %q\n", val) 95 | } 96 | } 97 | } 98 | 99 | func TestEncodeFloat64(t *testing.T) { 100 | var ( 101 | source PhpValue 102 | val string 103 | err error 104 | ) 105 | 106 | source = 42.378900000000002 107 | encoder := NewSerializer() 108 | if val, err = encoder.Encode(source); err != nil { 109 | t.Errorf("Error while encoding float value: %v\n", err) 110 | } else { 111 | if val != "d:42.378900000000002;" { 112 | t.Errorf("Float value decoded incorrectly, have got %q\n", val) 113 | } 114 | } 115 | } 116 | 117 | func TestEncodeFloat64Minus(t *testing.T) { 118 | var ( 119 | source PhpValue 120 | val string 121 | err error 122 | ) 123 | 124 | source = -42.378900000000002 125 | encoder := NewSerializer() 126 | if val, err = encoder.Encode(source); err != nil { 127 | t.Errorf("Error while encoding float value: %v\n", err) 128 | } else { 129 | if val != "d:-42.378900000000002;" { 130 | t.Errorf("Float value decoded incorrectly, have got %q\n", val) 131 | } 132 | } 133 | } 134 | 135 | func TestEncodeString(t *testing.T) { 136 | var ( 137 | source PhpValue 138 | val string 139 | err error 140 | ) 141 | 142 | source = "foobar" 143 | encoder := NewSerializer() 144 | if val, err = encoder.Encode(source); err != nil { 145 | t.Errorf("Error while encoding string value: %v\n", err) 146 | } else { 147 | if val != "s:6:\"foobar\";" { 148 | t.Errorf("String value decoded incorrectly, have got %q\n", val) 149 | } 150 | } 151 | } 152 | 153 | func TestEncodeArray(t *testing.T) { 154 | var ( 155 | source PhpValue 156 | val string 157 | err error 158 | ) 159 | 160 | source = PhpArray{ 161 | 0: 10, 162 | 1: 11, 163 | 2: 12, 164 | } 165 | encoder := NewSerializer() 166 | if val, err = encoder.Encode(source); err != nil { 167 | t.Errorf("Error while encoding array value: %v\n", err) 168 | } else { 169 | if !strings.Contains(val, "i:0;i:10;") { 170 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "i:0;i:10;", val) 171 | } else if !strings.Contains(val, "i:1;i:11;") { 172 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "i:1;i:11;", val) 173 | } else if !strings.Contains(val, "i:2;i:12;") { 174 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "i:2;i:12;", val) 175 | } 176 | } 177 | } 178 | 179 | func TestEncodeArray2(t *testing.T) { 180 | var ( 181 | source PhpValue 182 | val string 183 | err error 184 | ) 185 | 186 | source = map[PhpValue]PhpValue{ 187 | 0: 10, 188 | 1: 11, 189 | 2: 12, 190 | } 191 | encoder := NewSerializer() 192 | if val, err = encoder.Encode(source); err != nil { 193 | t.Errorf("Error while encoding array value: %v\n", err) 194 | } else { 195 | if !strings.Contains(val, "i:0;i:10;") { 196 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "i:0;i:10;", val) 197 | } else if !strings.Contains(val, "i:1;i:11;") { 198 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "i:1;i:11;", val) 199 | } else if !strings.Contains(val, "i:2;i:12;") { 200 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "i:2;i:12;", val) 201 | } 202 | } 203 | } 204 | 205 | func TestEncodeArrayMap(t *testing.T) { 206 | var ( 207 | source PhpValue 208 | val string 209 | err error 210 | ) 211 | 212 | source = PhpArray{ 213 | "foo": 4, 214 | "bar": 2, 215 | } 216 | encoder := NewSerializer() 217 | if val, err = encoder.Encode(source); err != nil { 218 | t.Errorf("Error while encoding array value: %v\n", err) 219 | } else { 220 | if !strings.Contains(val, "s:3:\"foo\";i:4;") { 221 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:3:\"foo\";i:4;", val) 222 | } else if !strings.Contains(val, "s:3:\"bar\";i:2;") { 223 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:3:\"bar\";i:2;", val) 224 | } 225 | } 226 | } 227 | 228 | func TestEncodeArrayArray(t *testing.T) { 229 | var ( 230 | source PhpValue 231 | val string 232 | err error 233 | ) 234 | 235 | source = PhpArray{ 236 | "foo": PhpArray{ 237 | 0: 10, 238 | }, 239 | "bar": 2, 240 | } 241 | encoder := NewSerializer() 242 | if val, err = encoder.Encode(source); err != nil { 243 | t.Errorf("Error while encoding array value: %v\n", err) 244 | } else { 245 | if !strings.Contains(val, "s:3:\"foo\";a:1:{i:0;i:10;}") { 246 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:3:\"foo\";a:1:{i:0;i:10;}", val) 247 | } else if !strings.Contains(val, "s:3:\"bar\";i:2;") { 248 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:3:\"bar\";i:2;", val) 249 | } 250 | } 251 | } 252 | 253 | func TestEncodeObject(t *testing.T) { 254 | var ( 255 | source PhpValue 256 | val string 257 | err error 258 | ) 259 | 260 | obj := NewPhpObject("Test") 261 | obj.SetPublic("public", 1) 262 | obj.SetProtected("protected", 2) 263 | obj.SetPrivate("private", 3) 264 | 265 | source = obj 266 | encoder := NewSerializer() 267 | if val, err = encoder.Encode(source); err != nil { 268 | t.Errorf("Error while encoding array value: %v\n", err) 269 | } else { 270 | if !strings.Contains(val, "O:4:\"Test\"") { 271 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "O:4:\"Test\"", val) 272 | } else if !strings.Contains(val, "s:6:\"public\";i:1;") { 273 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:6:\"public\";i:1;", val) 274 | } else if !strings.Contains(val, "s:12:\"\x00*\x00protected\";i:2;") { 275 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:12:\"\x00*\x00protected\";i:2;", val) 276 | } else if !strings.Contains(val, "s:13:\"\x00Test\x00private\";i:3;") { 277 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:13:\"\x00Test\x00private\";i:3;", val) 278 | } 279 | } 280 | } 281 | 282 | func TestEncodeArrayOfObjects(t *testing.T) { 283 | var ( 284 | source PhpValue 285 | val string 286 | err error 287 | ) 288 | 289 | obj1 := NewPhpObject("Test1") 290 | obj1.SetPublic("public", 11) 291 | obj1.SetProtected("protected", 12) 292 | obj1.SetPrivate("private", 13) 293 | 294 | obj2 := NewPhpObject("Test2") 295 | obj2.SetPublic("public", 21) 296 | obj2.SetProtected("protected", 22) 297 | obj2.SetPrivate("private", 23) 298 | 299 | source = PhpArray{ 300 | 0: obj1, 301 | 1: obj2, 302 | } 303 | encoder := NewSerializer() 304 | if val, err = encoder.Encode(source); err != nil { 305 | t.Errorf("Error while encoding array value: %v\n", err) 306 | } else { 307 | if !strings.Contains(val, "a:2:") { 308 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "a:2:", val) 309 | } else if !strings.Contains(val, "i:0;O:5:\"Test1\"") { 310 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "i:0;O:5:\"Test1\"", val) 311 | } else if !strings.Contains(val, "s:6:\"public\";i:11;") { 312 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:6:\"public\";i:11;", val) 313 | } else if !strings.Contains(val, "s:12:\"\x00*\x00protected\";i:12;") { 314 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:12:\"\x00*\x00protected\";i:12;", val) 315 | } else if !strings.Contains(val, "s:14:\"\x00Test1\x00private\";i:13;") { 316 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:14:\"\x00Test1\x00private\";i:13;", val) 317 | } else if !strings.Contains(val, "i:1;O:5:\"Test2\"") { 318 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "i:1;O:5:\"Test2\"", val) 319 | } else if !strings.Contains(val, "s:6:\"public\";i:21;") { 320 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:6:\"public\";i:21;", val) 321 | } else if !strings.Contains(val, "s:12:\"\x00*\x00protected\";i:22;") { 322 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:12:\"\x00*\x00protected\";i:22;", val) 323 | } else if !strings.Contains(val, "s:14:\"\x00Test2\x00private\";i:23;") { 324 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:14:\"\x00Test2\x00private\";i:23;", val) 325 | } 326 | } 327 | } 328 | 329 | func TestEncodeObjectSerializable(t *testing.T) { 330 | var ( 331 | source PhpValue 332 | val string 333 | err error 334 | ) 335 | 336 | obj := NewPhpObjectSerialized("TestSerializable") 337 | obj.SetData("foobar") 338 | 339 | source = obj 340 | encoder := NewSerializer() 341 | if val, err = encoder.Encode(source); err != nil { 342 | t.Errorf("Error while encoding array value: %v\n", err) 343 | } else { 344 | if !strings.Contains(val, "C:16:\"TestSerializable\":6:{foobar}") { 345 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "C:16:\"TestSerializable\":6:{foobar}", val) 346 | } 347 | } 348 | } 349 | 350 | func TestEncodeObjectSerializableArray(t *testing.T) { 351 | var ( 352 | source PhpValue 353 | val string 354 | err error 355 | ) 356 | 357 | obj := NewPhpObjectSerialized("TestSerializable1") 358 | obj.SetValue(PhpArray{ 359 | "foo": 4, 360 | "bar": 2, 361 | }) 362 | 363 | source = obj 364 | encoder := NewSerializer() 365 | encoder.SetSerializedEncodeFunc(SerializedEncodeFunc(Serialize)) 366 | if val, err = encoder.Encode(source); err != nil { 367 | t.Errorf("Error while encoding array value: %v\n", err) 368 | } else { 369 | if !strings.Contains(val, "C:17:\"TestSerializable1\"") { 370 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "C:17:\"TestSerializable1\"", val) 371 | } else if !strings.Contains(val, "s:3:\"foo\";i:4;") { 372 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:3:\"foo\";i:4;", val) 373 | } else if !strings.Contains(val, "s:3:\"bar\";i:2;") { 374 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "s:3:\"bar\";i:2;", val) 375 | } 376 | } 377 | } 378 | 379 | func TestEncodeObjectSerializableJSON(t *testing.T) { 380 | var ( 381 | source PhpValue 382 | val string 383 | err error 384 | f SerializedEncodeFunc 385 | ) 386 | 387 | f = func(v PhpValue) (string, error) { 388 | var ( 389 | res []byte 390 | err error 391 | ) 392 | res, err = json.Marshal(v) 393 | return string(res), err 394 | } 395 | 396 | obj := NewPhpObjectSerialized("TestSerializable2") 397 | obj.SetValue(map[string]int{ 398 | "foo": 4, 399 | "bar": 2, 400 | }) 401 | 402 | source = obj 403 | encoder := NewSerializer() 404 | encoder.SetSerializedEncodeFunc(f) 405 | if val, err = encoder.Encode(source); err != nil { 406 | t.Errorf("Error while encoding array value: %v\n", err) 407 | } else { 408 | if !strings.Contains(val, "C:17:\"TestSerializable2\"") { 409 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "C:17:\"TestSerializable2\"", val) 410 | } else if !strings.Contains(val, "\"foo\":4") { 411 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "\"foo\":4", val) 412 | } else if !strings.Contains(val, "\"bar\":2") { 413 | t.Errorf("Array value decoded incorrectly, expected substring %q but have got %q\n", "\"bar\":2", val) 414 | } 415 | } 416 | } 417 | 418 | func TestEncodeSplArray(t *testing.T) { 419 | obj := NewPhpSplArray(PhpArray{"foo": 42}, nil) 420 | 421 | data, err := Serialize(obj) 422 | if err != nil { 423 | t.Errorf("Error while encoding array object: %v\n", err) 424 | } 425 | 426 | expected := "x:i:0;a:1:{s:3:\"foo\";i:42;};m:a:0:{}" 427 | 428 | if data != expected { 429 | t.Errorf("SplArray decoded incorrectly, expected: %q, got: %q\n", expected, data) 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /php_serialize/unserialize.go: -------------------------------------------------------------------------------- 1 | package php_serialize 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | const UNSERIALIZABLE_OBJECT_MAX_LEN = 10 * 1024 * 1024 * 1024 12 | 13 | func UnSerialize(s string) (PhpValue, error) { 14 | decoder := NewUnSerializer(s) 15 | decoder.SetSerializedDecodeFunc(SerializedDecodeFunc(UnSerialize)) 16 | return decoder.Decode() 17 | } 18 | 19 | type UnSerializer struct { 20 | source string 21 | r *strings.Reader 22 | lastErr error 23 | decodeFunc SerializedDecodeFunc 24 | } 25 | 26 | func NewUnSerializer(data string) *UnSerializer { 27 | return &UnSerializer{ 28 | source: data, 29 | } 30 | } 31 | 32 | func (self *UnSerializer) SetReader(r *strings.Reader) { 33 | self.r = r 34 | } 35 | 36 | func (self *UnSerializer) SetSerializedDecodeFunc(f SerializedDecodeFunc) { 37 | self.decodeFunc = f 38 | } 39 | 40 | func (self *UnSerializer) Decode() (PhpValue, error) { 41 | if self.r == nil { 42 | self.r = strings.NewReader(self.source) 43 | } 44 | 45 | var value PhpValue 46 | 47 | if token, _, err := self.r.ReadRune(); err == nil { 48 | switch token { 49 | default: 50 | self.saveError(fmt.Errorf("php_serialize: Unknown token %#U", token)) 51 | case TOKEN_NULL: 52 | value = self.decodeNull() 53 | case TOKEN_BOOL: 54 | value = self.decodeBool() 55 | case TOKEN_INT: 56 | value = self.decodeNumber(false) 57 | case TOKEN_FLOAT: 58 | value = self.decodeNumber(true) 59 | case TOKEN_STRING: 60 | value = self.decodeString(DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, true) 61 | case TOKEN_ARRAY: 62 | value = self.decodeArray() 63 | case TOKEN_OBJECT: 64 | value = self.decodeObject() 65 | case TOKEN_OBJECT_SERIALIZED: 66 | value = self.decodeSerialized() 67 | case TOKEN_REFERENCE, TOKEN_REFERENCE_OBJECT: 68 | value = self.decodeReference() 69 | case TOKEN_SPL_ARRAY: 70 | value = self.decodeSplArray() 71 | 72 | } 73 | } 74 | 75 | return value, self.lastErr 76 | } 77 | 78 | func (self *UnSerializer) decodeNull() PhpValue { 79 | self.expect(SEPARATOR_VALUES) 80 | return nil 81 | } 82 | 83 | func (self *UnSerializer) decodeBool() PhpValue { 84 | var ( 85 | raw rune 86 | err error 87 | ) 88 | self.expect(SEPARATOR_VALUE_TYPE) 89 | 90 | if raw, _, err = self.r.ReadRune(); err != nil { 91 | self.saveError(fmt.Errorf("php_serialize: Error while reading bool value: %v", err)) 92 | } 93 | 94 | self.expect(SEPARATOR_VALUES) 95 | return raw == '1' 96 | } 97 | 98 | func (self *UnSerializer) decodeNumber(isFloat bool) PhpValue { 99 | var ( 100 | raw string 101 | err error 102 | val PhpValue 103 | ) 104 | self.expect(SEPARATOR_VALUE_TYPE) 105 | 106 | if raw, err = self.readUntil(SEPARATOR_VALUES); err != nil { 107 | self.saveError(fmt.Errorf("php_serialize: Error while reading number value: %v", err)) 108 | } else { 109 | if isFloat { 110 | if val, err = strconv.ParseFloat(raw, 64); err != nil { 111 | self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to float: %v", raw, err)) 112 | } 113 | } else { 114 | if val, err = strconv.Atoi(raw); err != nil { 115 | self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to int: %v", raw, err)) 116 | } 117 | } 118 | } 119 | 120 | return val 121 | } 122 | 123 | func (self *UnSerializer) decodeString(left, right rune, isFinal bool) PhpValue { 124 | var ( 125 | err error 126 | val PhpValue 127 | strLen int 128 | readLen int 129 | ) 130 | 131 | strLen = self.readLen() 132 | self.expect(left) 133 | 134 | if strLen > 0 { 135 | buf := make([]byte, strLen, strLen) 136 | if readLen, err = self.r.Read(buf); err != nil { 137 | self.saveError(fmt.Errorf("php_serialize: Error while reading string value: %v", err)) 138 | } else { 139 | if readLen != strLen { 140 | self.saveError(fmt.Errorf("php_serialize: Unable to read string. Expected %d but have got %d bytes", strLen, readLen)) 141 | } else { 142 | val = string(buf) 143 | } 144 | } 145 | } 146 | 147 | self.expect(right) 148 | if isFinal { 149 | self.expect(SEPARATOR_VALUES) 150 | } 151 | return val 152 | } 153 | 154 | func (self *UnSerializer) decodeArray() PhpValue { 155 | var arrLen int 156 | val := make(PhpArray) 157 | 158 | arrLen = self.readLen() 159 | self.expect(DELIMITER_OBJECT_LEFT) 160 | 161 | for i := 0; i < arrLen; i++ { 162 | k, errKey := self.Decode() 163 | v, errVal := self.Decode() 164 | 165 | if errKey == nil && errVal == nil { 166 | val[k] = v 167 | /*switch t := k.(type) { 168 | default: 169 | self.saveError(fmt.Errorf("php_serialize: Unexpected key type %T", t)) 170 | case string: 171 | stringKey, _ := k.(string) 172 | val[stringKey] = v 173 | case int: 174 | intKey, _ := k.(int) 175 | val[strconv.Itoa(intKey)] = v 176 | }*/ 177 | } else { 178 | self.saveError(fmt.Errorf("php_serialize: Error while reading key or(and) value of array")) 179 | } 180 | } 181 | 182 | self.expect(DELIMITER_OBJECT_RIGHT) 183 | return val 184 | } 185 | 186 | func (self *UnSerializer) decodeObject() PhpValue { 187 | val := &PhpObject{ 188 | className: self.readClassName(), 189 | } 190 | 191 | rawMembers := self.decodeArray() 192 | val.members, _ = rawMembers.(PhpArray) 193 | 194 | return val 195 | } 196 | 197 | func (self *UnSerializer) decodeSerialized() PhpValue { 198 | val := &PhpObjectSerialized{ 199 | className: self.readClassName(), 200 | } 201 | 202 | rawData := self.decodeString(DELIMITER_OBJECT_LEFT, DELIMITER_OBJECT_RIGHT, false) 203 | val.data, _ = rawData.(string) 204 | 205 | if self.decodeFunc != nil && val.data != "" { 206 | var err error 207 | if val.value, err = self.decodeFunc(val.data); err != nil { 208 | self.saveError(err) 209 | } 210 | } 211 | 212 | return val 213 | } 214 | 215 | func (self *UnSerializer) decodeReference() PhpValue { 216 | self.expect(SEPARATOR_VALUE_TYPE) 217 | if _, err := self.readUntil(SEPARATOR_VALUES); err != nil { 218 | self.saveError(fmt.Errorf("php_serialize: Error while reading reference value: %v", err)) 219 | } 220 | return nil 221 | } 222 | 223 | func (self *UnSerializer) expect(expected rune) { 224 | if token, _, err := self.r.ReadRune(); err != nil { 225 | self.saveError(fmt.Errorf("php_serialize: Error while reading expected rune %#U: %v", expected, err)) 226 | } else if token != expected { 227 | if debugMode { 228 | log.Printf("php_serialize: source\n%s\n", self.source) 229 | log.Printf("php_serialize: reader info\n%#v\n", self.r) 230 | } 231 | self.saveError(fmt.Errorf("php_serialize: Expected %#U but have got %#U", expected, token)) 232 | } 233 | } 234 | 235 | func (self *UnSerializer) readUntil(stop rune) (string, error) { 236 | var ( 237 | token rune 238 | err error 239 | ) 240 | buf := bytes.NewBuffer([]byte{}) 241 | 242 | for { 243 | if token, _, err = self.r.ReadRune(); err != nil || token == stop { 244 | break 245 | } else { 246 | buf.WriteRune(token) 247 | } 248 | } 249 | 250 | return buf.String(), err 251 | } 252 | 253 | func (self *UnSerializer) readLen() int { 254 | var ( 255 | raw string 256 | err error 257 | val int 258 | ) 259 | self.expect(SEPARATOR_VALUE_TYPE) 260 | 261 | if raw, err = self.readUntil(SEPARATOR_VALUE_TYPE); err != nil { 262 | self.saveError(fmt.Errorf("php_serialize: Error while reading lenght of value: %v", err)) 263 | } else { 264 | if val, err = strconv.Atoi(raw); err != nil { 265 | self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to int: %v", raw, err)) 266 | } else if val > UNSERIALIZABLE_OBJECT_MAX_LEN { 267 | self.saveError(fmt.Errorf("php_serialize: Unserializable object length looks too big(%d). If you are sure you wanna unserialise it, please increase UNSERIALIZABLE_OBJECT_MAX_LEN const", val)) 268 | val = 0 269 | } 270 | } 271 | return val 272 | } 273 | 274 | func (self *UnSerializer) readClassName() (res string) { 275 | rawClass := self.decodeString(DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, false) 276 | res, _ = rawClass.(string) 277 | return 278 | } 279 | 280 | func (self *UnSerializer) saveError(err error) { 281 | if self.lastErr == nil { 282 | self.lastErr = err 283 | } 284 | } 285 | 286 | func (self *UnSerializer) decodeSplArray() PhpValue { 287 | var err error 288 | val := &PhpSplArray{} 289 | 290 | self.expect(SEPARATOR_VALUE_TYPE) 291 | self.expect(TOKEN_INT) 292 | 293 | flags := self.decodeNumber(false) 294 | if flags == nil { 295 | self.saveError(fmt.Errorf("php_serialize: Unable to read flags of SplArray")) 296 | return nil 297 | } 298 | val.flags = PhpValueInt(flags) 299 | 300 | if val.array, err = self.Decode(); err != nil { 301 | self.saveError(fmt.Errorf("php_serialize: Can't parse SplArray: %v", err)) 302 | return nil 303 | } 304 | 305 | self.expect(SEPARATOR_VALUES) 306 | self.expect(TOKEN_SPL_ARRAY_MEMBERS) 307 | self.expect(SEPARATOR_VALUE_TYPE) 308 | 309 | if val.properties, err = self.Decode(); err != nil { 310 | self.saveError(fmt.Errorf("php_serialize: Can't parse properties of SplArray: %v", err)) 311 | return nil 312 | } 313 | 314 | return val 315 | } 316 | -------------------------------------------------------------------------------- /php_serialize/unserialize_test.go: -------------------------------------------------------------------------------- 1 | package php_serialize 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | func TestDecodeNil(t *testing.T) { 9 | var ( 10 | val PhpValue 11 | err error 12 | ) 13 | 14 | decoder := NewUnSerializer("N;") 15 | if val, err = decoder.Decode(); err != nil { 16 | t.Errorf("Error while decoding nil value: %v\n", err) 17 | } else { 18 | if val != nil { 19 | t.Errorf("Nil value decoded incorrectly, have got %v\n", val) 20 | } 21 | } 22 | } 23 | 24 | func TestDecodeBoolTrue(t *testing.T) { 25 | var ( 26 | val PhpValue 27 | err error 28 | ) 29 | 30 | decoder := NewUnSerializer("b:1;") 31 | if val, err = decoder.Decode(); err != nil { 32 | t.Errorf("Error while decoding bool (true) value: %v\n", err) 33 | } else { 34 | if boolVal, ok := val.(bool); !ok { 35 | t.Errorf("Unable to convert %v to bool\n", val) 36 | } else if boolVal != true { 37 | t.Errorf("Bool (true) value decoded incorrectly, expected: %v, have got: %v\n", true, boolVal) 38 | } 39 | } 40 | } 41 | 42 | func TestDecodeBoolFalse(t *testing.T) { 43 | var ( 44 | val PhpValue 45 | err error 46 | ) 47 | 48 | decoder := NewUnSerializer("b:0;") 49 | if val, err = decoder.Decode(); err != nil { 50 | t.Errorf("Error while decoding bool (false) value: %v\n", err) 51 | } else { 52 | if boolVal, ok := val.(bool); !ok { 53 | t.Errorf("Unable to convert %v to bool\n", val) 54 | } else if boolVal != false { 55 | t.Errorf("Bool (false) value decoded incorrectly, expected: %v, have got: %v\n", false, boolVal) 56 | } 57 | } 58 | } 59 | 60 | func TestDecodeInt(t *testing.T) { 61 | var ( 62 | val PhpValue 63 | err error 64 | ) 65 | 66 | decoder := NewUnSerializer("i:42;") 67 | if val, err = decoder.Decode(); err != nil { 68 | t.Errorf("Error while decoding int value: %v\n", err) 69 | } else { 70 | if intVal, ok := val.(int); !ok { 71 | t.Errorf("Unable to convert %v to int\n", val) 72 | } else if intVal != 42 { 73 | t.Errorf("Int value decoded incorrectly, expected: %v, have got: %v\n", 42, intVal) 74 | } 75 | } 76 | } 77 | 78 | func TestDecodeIntMinus(t *testing.T) { 79 | var ( 80 | val PhpValue 81 | err error 82 | ) 83 | 84 | decoder := NewUnSerializer("i:-42;") 85 | if val, err = decoder.Decode(); err != nil { 86 | t.Errorf("Error while decoding int value: %v\n", err) 87 | } else { 88 | if intVal, ok := val.(int); !ok { 89 | t.Errorf("Unable to convert %v to int\n", val) 90 | } else if intVal != -42 { 91 | t.Errorf("Int value decoded incorrectly, expected: %v, have got: %v\n", -42, intVal) 92 | } 93 | } 94 | } 95 | 96 | func TestDecodeFloat64(t *testing.T) { 97 | var ( 98 | val PhpValue 99 | err error 100 | ) 101 | 102 | decoder := NewUnSerializer("d:42.378900000000002;") 103 | if val, err = decoder.Decode(); err != nil { 104 | t.Errorf("Error while decoding float4 value: %v\n", err) 105 | } else { 106 | if floatVal, ok := val.(float64); !ok { 107 | t.Errorf("Unable to convert %v to float\n", val) 108 | } else if floatVal != 42.378900000000002 { 109 | t.Errorf("Float64 value decoded incorrectly, expected: %v, have got: %v\n", 42.378900000000002, floatVal) 110 | } 111 | } 112 | } 113 | 114 | func TestDecodeFloat64Minus(t *testing.T) { 115 | var ( 116 | val PhpValue 117 | err error 118 | ) 119 | 120 | decoder := NewUnSerializer("d:-42.378900000000002;") 121 | if val, err = decoder.Decode(); err != nil { 122 | t.Errorf("Error while decoding float4 value: %v\n", err) 123 | } else { 124 | if floatVal, ok := val.(float64); !ok { 125 | t.Errorf("Unable to convert %v to float\n", val) 126 | } else if floatVal != -42.378900000000002 { 127 | t.Errorf("Float64 value decoded incorrectly, expected: %v, have got: %v\n", -42.378900000000002, floatVal) 128 | } 129 | } 130 | } 131 | 132 | func TestDecodeString(t *testing.T) { 133 | var ( 134 | val PhpValue 135 | err error 136 | ) 137 | 138 | decoder := NewUnSerializer("s:6:\"foobar\";") 139 | if val, err = decoder.Decode(); err != nil { 140 | t.Errorf("Error while decoding string value: %v\n", err) 141 | } else { 142 | if strVal, ok := val.(string); !ok { 143 | t.Errorf("Unable to convert %v to string\n", val) 144 | } else if strVal != "foobar" { 145 | t.Errorf("String value decoded incorrectly, expected: %v, have got: %v\n", "foobar", strVal) 146 | } 147 | } 148 | } 149 | 150 | func TestDecodeArray(t *testing.T) { 151 | var ( 152 | val PhpValue 153 | err error 154 | ) 155 | 156 | decoder := NewUnSerializer("a:3:{i:0;i:10;i:1;i:11;i:2;i:12;}") 157 | if val, err = decoder.Decode(); err != nil { 158 | t.Errorf("Error while decoding array value: %v\n", err) 159 | } else { 160 | if arrVal, ok := val.(PhpArray); !ok { 161 | t.Errorf("Unable to convert %v to PhpArray\n", val) 162 | } else if v1, ok1 := arrVal[PhpValue(0)]; !ok1 { 163 | t.Errorf("Array value decoded incorrectly, key `0` doest not exists\n") 164 | } else if intV1, ok1 := v1.(int); !ok1 { 165 | t.Errorf("Unable to convert %v to int\n", v1) 166 | } else if intV1 != 10 { 167 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 10, intV1) 168 | } else if v2, ok2 := arrVal[PhpValue(1)]; !ok2 { 169 | t.Errorf("Array value decoded incorrectly, key `1` doest not exists\n") 170 | } else if intV2, ok2 := v2.(int); !ok2 { 171 | t.Errorf("Unable to convert %v to int\n", v2) 172 | } else if intV2 != 11 { 173 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 11, intV2) 174 | } else if v3, ok3 := arrVal[PhpValue(2)]; !ok3 { 175 | t.Errorf("Array value decoded incorrectly, key `2` doest not exists\n") 176 | } else if intV3, ok3 := v3.(int); !ok3 { 177 | t.Errorf("Unable to convert %v to int\n", v3) 178 | } else if intV3 != 12 { 179 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 12, intV3) 180 | } 181 | } 182 | } 183 | 184 | func TestDecodeArrayMap(t *testing.T) { 185 | var ( 186 | val PhpValue 187 | err error 188 | ) 189 | 190 | decoder := NewUnSerializer("a:2:{s:3:\"foo\";i:4;s:3:\"bar\";i:2;}") 191 | if val, err = decoder.Decode(); err != nil { 192 | t.Errorf("Error while decoding array value: %v\n", err) 193 | } else { 194 | if arrVal, ok := val.(PhpArray); !ok { 195 | t.Errorf("Unable to convert %v to PhpArray\n", val) 196 | } else if v1, ok1 := arrVal["foo"]; !ok1 { 197 | t.Errorf("Array value decoded incorrectly, key `foo` doest not exists\n") 198 | } else if intV1, ok1 := v1.(int); !ok1 { 199 | t.Errorf("Unable to convert %v to int\n", v1) 200 | } else if intV1 != 4 { 201 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 4, intV1) 202 | } else if v2, ok2 := arrVal["bar"]; !ok2 { 203 | t.Errorf("Array value decoded incorrectly, key `bar` doest not exists\n") 204 | } else if intV2, ok2 := v2.(int); !ok2 { 205 | t.Errorf("Unable to convert %v to int\n", v2) 206 | } else if intV2 != 2 { 207 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 2, intV2) 208 | } 209 | } 210 | } 211 | 212 | func TestDecodeArrayArray(t *testing.T) { 213 | var ( 214 | val PhpValue 215 | err error 216 | ) 217 | 218 | decoder := NewUnSerializer("a:2:{s:3:\"foo\";a:3:{i:0;i:10;i:1;i:11;i:2;i:12;}s:3:\"bar\";i:2;}") 219 | if val, err = decoder.Decode(); err != nil { 220 | t.Errorf("Error while decoding array value: %v\n", err) 221 | } else { 222 | if arrVal, ok := val.(PhpArray); !ok { 223 | t.Errorf("Unable to convert %v to PhpArray\n", val) 224 | } else if v1, ok1 := arrVal["foo"]; !ok1 { 225 | t.Errorf("Array value decoded incorrectly, key `foo` doest not exists\n") 226 | } else if innerArr, ok1 := v1.(PhpArray); !ok1 { 227 | t.Errorf("Unable to convert %v to inner PhpArray\n", v1) 228 | } else if inv1, inOk1 := innerArr[PhpValue(0)]; !inOk1 { 229 | t.Errorf("Array value decoded incorrectly, key `0` doest not exists\n") 230 | } else if inIntV1, inOk1 := inv1.(int); !inOk1 { 231 | t.Errorf("Unable to convert %v to int\n", inv1) 232 | } else if inIntV1 != 10 { 233 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 10, inIntV1) 234 | } else if inv2, inOk2 := innerArr[PhpValue(1)]; !inOk2 { 235 | t.Errorf("Array value decoded incorrectly, key `1` doest not exists\n") 236 | } else if inIntV2, inOk2 := inv2.(int); !inOk2 { 237 | t.Errorf("Unable to convert %v to int\n", inv2) 238 | } else if inIntV2 != 11 { 239 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 11, inIntV2) 240 | } else if inv3, inOk3 := innerArr[PhpValue(2)]; !inOk3 { 241 | t.Errorf("Array value decoded incorrectly, key `2` doest not exists\n") 242 | } else if inIntV3, inOk3 := inv3.(int); !inOk3 { 243 | t.Errorf("Unable to convert %v to int\n", inv3) 244 | } else if inIntV3 != 12 { 245 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 12, inIntV3) 246 | } else if v2, ok2 := arrVal["bar"]; !ok2 { 247 | t.Errorf("Array value decoded incorrectly, key `bar` doest not exists\n") 248 | } else if intV2, ok2 := v2.(int); !ok2 { 249 | t.Errorf("Unable to convert %v to int\n", v2) 250 | } else if intV2 != 2 { 251 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 2, intV2) 252 | } 253 | } 254 | } 255 | 256 | func TestDecodeObject(t *testing.T) { 257 | var ( 258 | val PhpValue 259 | err error 260 | ) 261 | 262 | decoder := NewUnSerializer("O:4:\"Test\":3:{s:6:\"public\";i:1;s:12:\"\x00*\x00protected\";i:2;s:13:\"\x00Test\x00private\";i:3;}") 263 | if val, err = decoder.Decode(); err != nil { 264 | t.Errorf("Error while decoding object value: %v\n", err) 265 | } else { 266 | if obj, ok := val.(*PhpObject); !ok { 267 | t.Errorf("Unable to convert %v to *PhpObject\n", val) 268 | } else if obj.GetClassName() != "Test" { 269 | t.Errorf("Object class name decoded incorrectly, expected: %s, have got: %s\n", "Test", obj.GetClassName()) 270 | } else if value1, ok := obj.GetPublic("public"); !ok { 271 | t.Errorf("Public member of object was decoded incorrectly, key `publice` doest not exists\n") 272 | } else if intV1, ok := value1.(int); !ok { 273 | t.Errorf("Unable to convert %v to int\n", value1) 274 | } else if intV1 != 1 { 275 | t.Errorf("Public member of object was decoded incorrectly, expected: %v, have got: %v\n", 1, intV1) 276 | } else if value2, ok := obj.GetProtected("protected"); !ok { 277 | t.Errorf("Protected member of object was decoded incorrectly, key `protected` doest not exists\n") 278 | } else if intV2, ok := value2.(int); !ok { 279 | t.Errorf("Unable to convert %v to int\n", value2) 280 | } else if intV2 != 2 { 281 | t.Errorf("Protected member of object was decoded incorrectly, expected: %v, have got: %v\n", 2, intV2) 282 | } else if value3, ok := obj.GetPrivate("private"); !ok { 283 | t.Errorf("Private member of object was decoded incorrectly, key `private` doest not exists\n") 284 | } else if intV3, ok := value3.(int); !ok { 285 | t.Errorf("Unable to convert %v to int\n", value3) 286 | } else if intV3 != 3 { 287 | t.Errorf("Private member of object was decoded incorrectly, expected: %v, have got: %v\n", 3, intV3) 288 | } 289 | } 290 | } 291 | 292 | func TestDecodeArrayOfObjects(t *testing.T) { 293 | var ( 294 | val PhpValue 295 | err error 296 | ) 297 | 298 | decoder := NewUnSerializer("a:2:{i:0;O:5:\"Test1\":3:{s:6:\"public\";i:11;s:12:\"\x00*\x00protected\";i:12;s:14:\"\x00Test1\x00private\";i:13;}i:1;O:5:\"Test2\":3:{s:6:\"public\";i:21;s:12:\"\x00*\x00protected\";i:22;s:14:\"\x00Test2\x00private\";i:23;}}") 299 | if val, err = decoder.Decode(); err != nil { 300 | t.Errorf("Error while decoding array of objects value: %v\n", err) 301 | } else { 302 | if arrVal, ok := val.(PhpArray); !ok { 303 | t.Errorf("Unable to convert %v to PhpArray\n", val) 304 | } else if v1, ok1 := arrVal[PhpValue(0)]; !ok1 { 305 | t.Errorf("Array value decoded incorrectly, key `0` doest not exists\n") 306 | } else if obj1, ok1 := v1.(*PhpObject); !ok1 { 307 | t.Errorf("Unable to convert %v to *PhpObject\n", v1) 308 | } else if obj1.GetClassName() != "Test1" { 309 | t.Errorf("Object class name decoded incorrectly, expected: %s, have got: %s\n", "Test1", obj1.GetClassName()) 310 | } else if value1, ok := obj1.GetPublic("public"); !ok { 311 | t.Errorf("Public member of object was decoded incorrectly, key `publice` doest not exists\n") 312 | } else if intV1, ok := value1.(int); !ok { 313 | t.Errorf("Unable to convert %v to int\n", value1) 314 | } else if intV1 != 11 { 315 | t.Errorf("Public member of object was decoded incorrectly, expected: %v, have got: %v\n", 11, intV1) 316 | } else if value2, ok := obj1.GetProtected("protected"); !ok { 317 | t.Errorf("Protected member of object was decoded incorrectly, key `protected` doest not exists\n") 318 | } else if intV2, ok := value2.(int); !ok { 319 | t.Errorf("Unable to convert %v to int\n", value2) 320 | } else if intV2 != 12 { 321 | t.Errorf("Protected member of object was decoded incorrectly, expected: %v, have got: %v\n", 12, intV2) 322 | } else if value3, ok := obj1.GetPrivate("private"); !ok { 323 | t.Errorf("Private member of object was decoded incorrectly, key `private` doest not exists\n") 324 | } else if intV3, ok := value3.(int); !ok { 325 | t.Errorf("Unable to convert %v to int\n", value3) 326 | } else if intV3 != 13 { 327 | t.Errorf("Private member of object was decoded incorrectly, expected: %v, have got: %v\n", 13, intV3) 328 | } else if v2, ok2 := arrVal[PhpValue(1)]; !ok2 { 329 | t.Errorf("Array value decoded incorrectly, key `1` doest not exists\n") 330 | } else if obj2, ok2 := v2.(*PhpObject); !ok2 { 331 | t.Errorf("Unable to convert %v to *PhpObject\n", v2) 332 | } else if obj2.GetClassName() != "Test2" { 333 | t.Errorf("Object class name decoded incorrectly, expected: %s, have got: %s\n", "Test2", obj2.GetClassName()) 334 | } else if value1, ok := obj2.GetPublic("public"); !ok { 335 | t.Errorf("Public member of object was decoded incorrectly, key `publice` doest not exists\n") 336 | } else if intV1, ok := value1.(int); !ok { 337 | t.Errorf("Unable to convert %v to int\n", value1) 338 | } else if intV1 != 21 { 339 | t.Errorf("Public member of object was decoded incorrectly, expected: %v, have got: %v\n", 21, intV1) 340 | } else if value2, ok := obj2.GetProtected("protected"); !ok { 341 | t.Errorf("Protected member of object was decoded incorrectly, key `protected` doest not exists\n") 342 | } else if intV2, ok := value2.(int); !ok { 343 | t.Errorf("Unable to convert %v to int\n", value2) 344 | } else if intV2 != 22 { 345 | t.Errorf("Protected member of object was decoded incorrectly, expected: %v, have got: %v\n", 22, intV2) 346 | } else if value3, ok := obj2.GetPrivate("private"); !ok { 347 | t.Errorf("Private member of object was decoded incorrectly, key `private` doest not exists\n") 348 | } else if intV3, ok := value3.(int); !ok { 349 | t.Errorf("Unable to convert %v to int\n", value3) 350 | } else if intV3 != 23 { 351 | t.Errorf("Private member of object was decoded incorrectly, expected: %v, have got: %v\n", 23, intV3) 352 | } 353 | } 354 | } 355 | 356 | func TestDecodeObjectSerializable(t *testing.T) { 357 | var ( 358 | val PhpValue 359 | err error 360 | ) 361 | 362 | decoder := NewUnSerializer("C:16:\"TestSerializable\":6:{foobar}") 363 | if val, err = decoder.Decode(); err != nil { 364 | t.Errorf("Error while decoding object value: %v\n", err) 365 | } else { 366 | if obj, ok := val.(*PhpObjectSerialized); !ok { 367 | t.Errorf("Unable to convert %v to *PhpObjectSerialized\n", val) 368 | } else if obj.GetClassName() != "TestSerializable" { 369 | t.Errorf("Object class name decoded incorrectly, expected: %s, have got: %s\n", "TestSerializable", obj.GetClassName()) 370 | } else if obj.GetData() != "foobar" { 371 | t.Errorf("Object value decoded incorrectly, expected: %v, have got: %v\n", "foobar", obj.GetData()) 372 | } 373 | } 374 | } 375 | 376 | func TestDecodeObjectSerializableArray(t *testing.T) { 377 | var ( 378 | val PhpValue 379 | err error 380 | ) 381 | 382 | decoder := NewUnSerializer("C:17:\"TestSerializable1\":34:{a:2:{s:3:\"foo\";i:4;s:3:\"bar\";i:2;}}") 383 | decoder.SetSerializedDecodeFunc(SerializedDecodeFunc(UnSerialize)) 384 | if val, err = decoder.Decode(); err != nil { 385 | t.Errorf("Error while decoding object value: %v\n", err) 386 | } else { 387 | if obj, ok := val.(*PhpObjectSerialized); !ok { 388 | t.Errorf("Unable to convert %v to *PhpObjectSerialized\n", val) 389 | } else if obj.GetClassName() != "TestSerializable1" { 390 | t.Errorf("Object class name decoded incorrectly, expected: %s, have got: %s\n", "TestSerializable1", obj.GetClassName()) 391 | } else if obj.GetData() != "a:2:{s:3:\"foo\";i:4;s:3:\"bar\";i:2;}" { 392 | t.Errorf("Object value decoded incorrectly, expected: %v, have got: %v\n", "a:2:{s:3:\"foo\";i:4;s:3:\"bar\";i:2;}", obj.GetData()) 393 | } else if vv := obj.GetValue(); vv == nil { 394 | t.Errorf("Object value decoded incorrectly, expected value as PhpArray, have got: %v\n", obj.GetValue()) 395 | } else if arrVal, ok := vv.(PhpArray); !ok { 396 | t.Errorf("Unable to convert %v to PhpArray\n", vv) 397 | } else if v1, ok1 := arrVal["foo"]; !ok1 { 398 | t.Errorf("Array value decoded incorrectly, key `foo` doest not exists\n") 399 | } else if intV1, ok1 := v1.(int); !ok1 { 400 | t.Errorf("Unable to convert %v to int\n", v1) 401 | } else if intV1 != 4 { 402 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 4, intV1) 403 | } else if v2, ok2 := arrVal["bar"]; !ok2 { 404 | t.Errorf("Array value decoded incorrectly, key `bar` doest not exists\n") 405 | } else if intV2, ok2 := v2.(int); !ok2 { 406 | t.Errorf("Unable to convert %v to int\n", v2) 407 | } else if intV2 != 2 { 408 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 2, intV2) 409 | } 410 | } 411 | } 412 | 413 | func TestDecodeObjectSerializableJSON(t *testing.T) { 414 | var ( 415 | val PhpValue 416 | err error 417 | f SerializedDecodeFunc 418 | ) 419 | 420 | f = func(s string) (PhpValue, error) { 421 | var ( 422 | val map[string]int 423 | err error 424 | ) 425 | err = json.Unmarshal([]byte(s), &val) 426 | return val, err 427 | } 428 | 429 | decoder := NewUnSerializer("C:17:\"TestSerializable2\":17:{{\"foo\":4,\"bar\":2}}") 430 | decoder.SetSerializedDecodeFunc(f) 431 | if val, err = decoder.Decode(); err != nil { 432 | t.Errorf("Error while decoding object value: %v\n", err) 433 | } else { 434 | if obj, ok := val.(*PhpObjectSerialized); !ok { 435 | t.Errorf("Unable to convert %v to *PhpObjectSerialized\n", val) 436 | } else if obj.GetClassName() != "TestSerializable2" { 437 | t.Errorf("Object class name decoded incorrectly, expected: %s, have got: %s\n", "TestSerializable2", obj.GetClassName()) 438 | } else if obj.GetData() != "{\"foo\":4,\"bar\":2}" { 439 | t.Errorf("Object value decoded incorrectly, expected: %v, have got: %v\n", "{\"foo\":4,\"bar\":2}", obj.GetData()) 440 | } else if vv := obj.GetValue(); vv == nil { 441 | t.Errorf("Object value decoded incorrectly, expected value as PhpArray, have got: %v\n", obj.GetValue()) 442 | } else if arrVal, ok := vv.(map[string]int); !ok { 443 | t.Errorf("Unable to convert %v to PhpArray\n", vv) 444 | } else if v1, ok1 := arrVal["foo"]; !ok1 { 445 | t.Errorf("Array value decoded incorrectly, key `foo` doest not exists\n") 446 | } else if v1 != 4 { 447 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 4, v1) 448 | } else if v2, ok2 := arrVal["bar"]; !ok2 { 449 | t.Errorf("Array value decoded incorrectly, key `bar` doest not exists\n") 450 | } else if v2 != 2 { 451 | t.Errorf("Array value decoded incorrectly, expected: %v, have got: %v\n", 2, v2) 452 | } 453 | } 454 | } 455 | 456 | func TestDecodeSplArray(t *testing.T) { 457 | val, err := UnSerialize("x:i:0;a:1:{s:3:\"foo\";s:3:\"bar\";};m:a:0:{}") 458 | if err != nil { 459 | t.Errorf("Can't decode array object: %v\n", err) 460 | } 461 | 462 | obj, ok := val.(*PhpSplArray) 463 | if !ok { 464 | t.Errorf("Unable to convert %v to *PhpSplArray", val) 465 | } 466 | 467 | array, ok := obj.GetArray().(PhpArray) 468 | if !ok { 469 | t.Errorf("Can't convert %v to PhpArray", obj.GetArray()) 470 | } 471 | 472 | if len(array) != 1 || array["foo"] != "bar" { 473 | t.Errorf("Can't find 'foo' key in %v", array) 474 | } 475 | 476 | properties, ok := obj.GetProperties().(PhpArray) 477 | if !ok { 478 | t.Errorf("Can't convert %v to PhpArray", obj.GetProperties()) 479 | } 480 | 481 | if len(properties) > 0 { 482 | t.Errorf("Expected empty PhpArray, got %v", properties) 483 | } 484 | } 485 | 486 | func TestDecodeSplArraySerialized(t *testing.T) { 487 | objValue, err := UnSerialize("C:11:\"ArrayObject\":21:{x:i:0;a:0:{};m:a:0:{}}") 488 | if err != nil { 489 | t.Errorf("Error while decoding object value: %v\n", err) 490 | } 491 | 492 | obj, ok := objValue.(*PhpObjectSerialized) 493 | if !ok { 494 | t.Errorf("Unable to convert %v to *PhpObjectSerialized\n", objValue) 495 | } 496 | 497 | array, ok := obj.GetValue().(*PhpSplArray) 498 | if !ok { 499 | t.Errorf("Unable to convert %v to *PhpSplArray\n", obj.GetValue()) 500 | } 501 | 502 | if array.flags != 0 { 503 | t.Errorf("SplArray flags expected: 0, got %v\n", array.flags) 504 | } 505 | 506 | arrayStorage, ok := array.array.(PhpArray) 507 | if !ok || arrayStorage == nil { 508 | t.Errorf("SplArray.array expected: empty PhpArray, got %v", array.array) 509 | } 510 | 511 | arrayProperties, ok := array.properties.(PhpArray) 512 | if !ok || arrayProperties == nil { 513 | t.Errorf("SplArray.properties expected: empty PhpArray, got %v", array.properties) 514 | } 515 | } 516 | --------------------------------------------------------------------------------