1<?php 2/** 3 * The Serialize:: class provides various methods of encapsulating data. 4 * 5 * Copyright 2001-2016 Horde LLC (http://www.horde.org/) 6 * 7 * See the enclosed file COPYING for license information (LGPL). If you 8 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 9 * 10 * @author Stephane Huther <shuther1@free.fr> 11 * @author Michael Slusarz <slusarz@horde.org> 12 * @package Serialize 13 * @category Horde 14 */ 15class Horde_Serialize 16{ 17 /* Type constants */ 18 const UNKNOWN = -1; 19 const NONE = 0; 20 const WDDX = 1; 21 const BZIP = 2; 22 const IMAP8 = 3; 23 const IMAPUTF7 = 4; 24 const IMAPUTF8 = 5; 25 const BASIC = 6; 26 const GZ_DEFLATE = 7; 27 const GZ_COMPRESS = 8; 28 const GZ_ENCODE = 9; 29 const BASE64 = 10; 30 const RAW = 12; 31 const URL = 13; 32 const UTF7 = 14; 33 const UTF7_BASIC = 15; 34 const JSON = 16; 35 const LZF = 17; 36 37 /** 38 * Serialize a value. 39 * 40 * See the list of constants at the top of the file for the serializing 41 * techniques that can be used. 42 * 43 * @param mixed $data The data to be serialized. 44 * @param mixed $mode The mode of serialization. Can be either a single 45 * mode or array of modes. If array, will be 46 * serialized in the order provided. 47 * @param mixed $params Any additional parameters the serialization method 48 * requires. 49 * 50 * @return string The serialized data. 51 * @throws Horde_Serialize_Exception 52 */ 53 public static function serialize($data, $mode = array(self::BASIC), 54 $params = null) 55 { 56 if (!is_array($mode)) { 57 $mode = array($mode); 58 } 59 60 /* Parse through the list of serializing modes. */ 61 foreach ($mode as $val) { 62 /* Check to make sure the mode is supported. */ 63 if (!self::hasCapability($val)) { 64 throw new Horde_Serialize_Exception('Unsupported serialization type'); 65 } 66 $data = self::_serialize($data, $val, $params); 67 } 68 69 return $data; 70 } 71 72 /** 73 * Unserialize a value. 74 * 75 * See the list of constants at the top of the file for the serializing 76 * techniques that can be used. 77 * 78 * @param mixed $data The data to be unserialized. 79 * @param mixed $mode The mode of unserialization. Can be either a 80 * single mode or array of modes. If array, will be 81 * unserialized in the order provided. 82 * @param mixed $params Any additional parameters the unserialization 83 * method requires. 84 * 85 * @return string The unserialized data. 86 * @throws Horde_Serialize_Exception 87 */ 88 public static function unserialize($data, $mode = self::BASIC, 89 $params = null) 90 { 91 if (!is_array($mode)) { 92 $mode = array($mode); 93 } 94 95 /* Parse through the list of unserializing modes. */ 96 foreach ($mode as $val) { 97 /* Check to make sure the mode is supported. */ 98 if (!self::hasCapability($val)) { 99 throw new Horde_Serialize_Exception('Unsupported unserialization type'); 100 } 101 $data = self::_unserialize($data, $val, $params); 102 } 103 104 return $data; 105 } 106 107 /** 108 * Check whether or not a serialization method is supported. 109 * 110 * @param integer $mode The serialization method. 111 * 112 * @return boolean True if supported, false if not. 113 */ 114 public static function hasCapability($mode) 115 { 116 switch ($mode) { 117 case self::BZIP: 118 return Horde_Util::extensionExists('bz2'); 119 120 case self::WDDX: 121 return Horde_Util::extensionExists('wddx'); 122 123 case self::IMAPUTF7: 124 return class_exists('Horde_Imap_Client'); 125 126 case self::IMAPUTF8: 127 return class_exists('Horde_Mime'); 128 129 case self::GZ_DEFLATE: 130 case self::GZ_COMPRESS: 131 case self::GZ_ENCODE: 132 return Horde_Util::extensionExists('zlib'); 133 134 case self::LZF: 135 return Horde_Util::extensionExists('lzf'); 136 137 case self::NONE: 138 case self::BASIC: 139 case self::BASE64: 140 case self::IMAP8: 141 case self::RAW: 142 case self::URL: 143 case self::UTF7: 144 case self::UTF7_BASIC: 145 case self::JSON: 146 return true; 147 148 default: 149 return false; 150 } 151 } 152 153 /** 154 * Serialize data. 155 * 156 * @param mixed $data The data to be serialized. 157 * @param mixed $mode The mode of serialization. Can be 158 * either a single mode or array of modes. 159 * If array, will be serialized in the 160 * order provided. 161 * @param mixed $params Any additional parameters the serialization method 162 * requires. 163 * 164 * @return string A serialized string. 165 * @throws Horde_Serialize_Exception 166 */ 167 protected static function _serialize($data, $mode, $params = null) 168 { 169 switch ($mode) { 170 case self::NONE: 171 break; 172 173 // $params['level'] = Level of compression (default: 3) 174 // $params['workfactor'] = How does compression phase behave when given 175 // worst case, highly repetitive, input data 176 // (default: 30) 177 case self::BZIP: 178 $data = bzcompress($data, isset($params['level']) ? $params['level'] : 3, isset($params['workfactor']) ? $params['workfactor'] : 30); 179 if (is_integer($data)) { 180 $data = false; 181 } 182 break; 183 184 case self::WDDX: 185 $data = wddx_serialize_value($data); 186 break; 187 188 case self::IMAP8: 189 $data = quoted_printable_encode($data); 190 break; 191 192 case self::IMAPUTF7: 193 $data = Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap(Horde_String::convertCharset($data, 'ISO-8859-1', 'UTF-8')); 194 break; 195 196 case self::IMAPUTF8: 197 $data = Horde_Mime::decode($data); 198 break; 199 200 // $params['level'] = Level of compression (default: 3) 201 case self::GZ_DEFLATE: 202 $data = gzdeflate($data, isset($params['level']) ? $params['level'] : 3); 203 break; 204 205 case self::BASIC: 206 $data = serialize($data); 207 break; 208 209 // $params['level'] = Level of compression (default: 3) 210 case self::GZ_COMPRESS: 211 $data = gzcompress($data, isset($params['level']) ? $params['level'] : 3); 212 break; 213 214 case self::BASE64: 215 $data = base64_encode($data); 216 break; 217 218 // $params['level'] = Level of compression (default: 3) 219 case self::GZ_ENCODE: 220 $data = gzencode($data, isset($params['level']) ? $params['level'] : 3); 221 break; 222 223 case self::RAW: 224 $data = rawurlencode($data); 225 break; 226 227 case self::URL: 228 $data = urlencode($data); 229 break; 230 231 // $params = Source character set 232 case self::UTF7: 233 $data = Horde_String::convertCharset($data, $params, 'UTF-7'); 234 break; 235 236 // $params = Source character set 237 case self::UTF7_BASIC: 238 $data = self::serialize($data, array(self::UTF7, self::BASIC), $params); 239 break; 240 241 case self::JSON: 242 $tmp = json_encode($data); 243 244 /* Basic error handling attempts. 245 * TODO: JSON_ERROR_UTF8 = 5; available as of PHP 5.3.3 */ 246 if (json_last_error() === 5) { 247 $data = json_encode(Horde_String::convertCharset($data, $params, 'UTF-8', true)); 248 } else { 249 $data = $tmp; 250 } 251 break; 252 253 case self::LZF: 254 $data = lzf_compress($data); 255 break; 256 } 257 258 if ($data === false) { 259 throw new Horde_Serialize_Exception('Serialization failed.'); 260 } 261 return $data; 262 } 263 264 /** 265 * Unserialize data. 266 * 267 * @param mixed $data The data to be unserialized. 268 * @param mixed $mode The mode of unserialization. Can be either a 269 * single mode or array of modes. If array, will be 270 * unserialized in the order provided. 271 * @param mixed $params Any additional parameters the unserialization 272 * method requires. 273 * 274 * @return mixed Unserialized data. 275 * @throws Horde_Serialize_Exception 276 */ 277 protected static function _unserialize(&$data, $mode, $params = null) 278 { 279 switch ($mode) { 280 case self::NONE: 281 break; 282 283 case self::RAW: 284 $data = rawurldecode($data); 285 break; 286 287 case self::URL: 288 $data = urldecode($data); 289 break; 290 291 case self::WDDX: 292 $data = wddx_deserialize($data); 293 break; 294 295 case self::BZIP: 296 // $params['small'] = Use bzip2 'small memory' mode? 297 $data = bzdecompress($data, isset($params['small']) ? $params['small'] : false); 298 break; 299 300 case self::IMAP8: 301 $data = quoted_printable_decode($data); 302 break; 303 304 case self::IMAPUTF7: 305 $data = Horde_String::convertCharset(Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($data), 'UTF-8', 'ISO-8859-1'); 306 break; 307 308 case self::IMAPUTF8: 309 $data = Horde_Mime::encode($data); 310 break; 311 312 case self::BASIC: 313 $data2 = @unserialize($data); 314 // Unserialize can return false both on error and if $data is the 315 // false value. 316 if (($data2 === false) && ($data == serialize(false))) { 317 return $data2; 318 } 319 $data = $data2; 320 break; 321 322 case self::GZ_DEFLATE: 323 $data = gzinflate($data); 324 break; 325 326 case self::BASE64: 327 $data = base64_decode($data); 328 break; 329 330 case self::GZ_COMPRESS: 331 $data = gzuncompress($data); 332 break; 333 334 // $params = Output character set 335 case self::UTF7: 336 $data = Horde_String::convertCharset($data, 'utf-7', $params); 337 break; 338 339 // $params = Output character set 340 case self::UTF7_BASIC: 341 $data = self::unserialize($data, array(self::BASIC, self::UTF7), $params); 342 break; 343 344 case self::JSON: 345 $out = json_decode($data); 346 if (!is_null($out) || (strcasecmp($data, 'null') === 0)) { 347 return $out; 348 } 349 break; 350 351 case self::LZF: 352 $data = @lzf_decompress($data); 353 break; 354 } 355 356 if ($data === false) { 357 throw new Horde_Serialize_Exception('Unserialization failed.'); 358 } 359 360 return $data; 361 } 362 363} 364