/framework/Serialize/lib/Horde/Serialize.php

https://github.com/imr/horde · PHP · 363 lines · 211 code · 52 blank · 100 comment · 18 complexity · 1e95c3b7305e8ff5550f1e807fe57ede MD5 · raw file

  1. <?php
  2. /**
  3. * The Serialize:: class provides various methods of encapsulating data.
  4. *
  5. * Copyright 2001-2014 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. */
  15. class 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. * Serialize a value.
  38. *
  39. * See the list of constants at the top of the file for the serializing
  40. * techniques that can be used.
  41. *
  42. * @param mixed $data The data to be serialized.
  43. * @param mixed $mode The mode of serialization. Can be either a single
  44. * mode or array of modes. If array, will be
  45. * serialized in the order provided.
  46. * @param mixed $params Any additional parameters the serialization method
  47. * requires.
  48. *
  49. * @return string The serialized data.
  50. * @throws Horde_Serialize_Exception
  51. */
  52. static public function serialize($data, $mode = array(self::BASIC),
  53. $params = null)
  54. {
  55. if (!is_array($mode)) {
  56. $mode = array($mode);
  57. }
  58. /* Parse through the list of serializing modes. */
  59. foreach ($mode as $val) {
  60. /* Check to make sure the mode is supported. */
  61. if (!self::hasCapability($val)) {
  62. throw new Horde_Serialize_Exception('Unsupported serialization type');
  63. }
  64. $data = self::_serialize($data, $val, $params);
  65. }
  66. return $data;
  67. }
  68. /**
  69. * Unserialize a value.
  70. *
  71. * See the list of constants at the top of the file for the serializing
  72. * techniques that can be used.
  73. *
  74. * @param mixed $data The data to be unserialized.
  75. * @param mixed $mode The mode of unserialization. Can be either a
  76. * single mode or array of modes. If array, will be
  77. * unserialized in the order provided.
  78. * @param mixed $params Any additional parameters the unserialization
  79. * method requires.
  80. *
  81. * @return string The unserialized data.
  82. * @throws Horde_Serialize_Exception
  83. */
  84. static public function unserialize($data, $mode = self::BASIC,
  85. $params = null)
  86. {
  87. if (!is_array($mode)) {
  88. $mode = array($mode);
  89. }
  90. /* Parse through the list of unserializing modes. */
  91. foreach ($mode as $val) {
  92. /* Check to make sure the mode is supported. */
  93. if (!self::hasCapability($val)) {
  94. throw new Horde_Serialize_Exception('Unsupported unserialization type');
  95. }
  96. $data = self::_unserialize($data, $val, $params);
  97. }
  98. return $data;
  99. }
  100. /**
  101. * Check whether or not a serialization method is supported.
  102. *
  103. * @param integer $mode The serialization method.
  104. *
  105. * @return boolean True if supported, false if not.
  106. */
  107. static public function hasCapability($mode)
  108. {
  109. switch ($mode) {
  110. case self::BZIP:
  111. return Horde_Util::extensionExists('bz2');
  112. case self::WDDX:
  113. return Horde_Util::extensionExists('wddx');
  114. case self::IMAPUTF7:
  115. return class_exists('Horde_Imap_Client');
  116. case self::IMAP8:
  117. case self::IMAPUTF8:
  118. return class_exists('Horde_Mime');
  119. case self::GZ_DEFLATE:
  120. case self::GZ_COMPRESS:
  121. case self::GZ_ENCODE:
  122. return Horde_Util::extensionExists('zlib');
  123. case self::LZF:
  124. return Horde_Util::extensionExists('lzf');
  125. case self::NONE:
  126. case self::BASIC:
  127. case self::BASE64:
  128. case self::RAW:
  129. case self::URL:
  130. case self::UTF7:
  131. case self::UTF7_BASIC:
  132. case self::JSON:
  133. return true;
  134. default:
  135. return false;
  136. }
  137. }
  138. /**
  139. * Serialize data.
  140. *
  141. * @param mixed $data The data to be serialized.
  142. * @param mixed $mode The mode of serialization. Can be
  143. * either a single mode or array of modes.
  144. * If array, will be serialized in the
  145. * order provided.
  146. * @param mixed $params Any additional parameters the serialization method
  147. * requires.
  148. *
  149. * @return string A serialized string.
  150. * @throws Horde_Serialize_Exception
  151. */
  152. static protected function _serialize($data, $mode, $params = null)
  153. {
  154. switch ($mode) {
  155. case self::NONE:
  156. break;
  157. // $params['level'] = Level of compression (default: 3)
  158. // $params['workfactor'] = How does compression phase behave when given
  159. // worst case, highly repetitive, input data
  160. // (default: 30)
  161. case self::BZIP:
  162. $data = bzcompress($data, isset($params['level']) ? $params['level'] : 3, isset($params['workfactor']) ? $params['workfactor'] : 30);
  163. if (is_integer($data)) {
  164. $data = false;
  165. }
  166. break;
  167. case self::WDDX:
  168. $data = wddx_serialize_value($data);
  169. break;
  170. case self::IMAP8:
  171. $data = Horde_Mime::quotedPrintableEncode($data);
  172. break;
  173. case self::IMAPUTF7:
  174. $data = Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap(Horde_String::convertCharset($data, 'ISO-8859-1', 'UTF-8'));
  175. break;
  176. case self::IMAPUTF8:
  177. $data = Horde_Mime::decode($data);
  178. break;
  179. // $params['level'] = Level of compression (default: 3)
  180. case self::GZ_DEFLATE:
  181. $data = gzdeflate($data, isset($params['level']) ? $params['level'] : 3);
  182. break;
  183. case self::BASIC:
  184. $data = serialize($data);
  185. break;
  186. // $params['level'] = Level of compression (default: 3)
  187. case self::GZ_COMPRESS:
  188. $data = gzcompress($data, isset($params['level']) ? $params['level'] : 3);
  189. break;
  190. case self::BASE64:
  191. $data = base64_encode($data);
  192. break;
  193. // $params['level'] = Level of compression (default: 3)
  194. case self::GZ_ENCODE:
  195. $data = gzencode($data, isset($params['level']) ? $params['level'] : 3);
  196. break;
  197. case self::RAW:
  198. $data = rawurlencode($data);
  199. break;
  200. case self::URL:
  201. $data = urlencode($data);
  202. break;
  203. // $params = Source character set
  204. case self::UTF7:
  205. $data = Horde_String::convertCharset($data, $params, 'UTF-7');
  206. break;
  207. // $params = Source character set
  208. case self::UTF7_BASIC:
  209. $data = self::serialize($data, array(self::UTF7, self::BASIC), $params);
  210. break;
  211. case self::JSON:
  212. $tmp = json_encode($data);
  213. /* Basic error handling attempts. Error code 5, although not
  214. * documented, indicates non UTF-8 data. */
  215. if (json_last_error() == 5) {
  216. $data = json_encode(Horde_String::convertCharset($data, $params, 'UTF-8', true));
  217. } else {
  218. $data = $tmp;
  219. }
  220. break;
  221. case self::LZF:
  222. $data = lzf_compress($data);
  223. break;
  224. }
  225. if ($data === false) {
  226. throw new Horde_Serialize_Exception('Serialization failed.');
  227. }
  228. return $data;
  229. }
  230. /**
  231. * Unserialize data.
  232. *
  233. * @param mixed $data The data to be unserialized.
  234. * @param mixed $mode The mode of unserialization. Can be either a
  235. * single mode or array of modes. If array, will be
  236. * unserialized in the order provided.
  237. * @param mixed $params Any additional parameters the unserialization
  238. * method requires.
  239. *
  240. * @return mixed Unserialized data.
  241. * @throws Horde_Serialize_Exception
  242. */
  243. static protected function _unserialize(&$data, $mode, $params = null)
  244. {
  245. switch ($mode) {
  246. case self::NONE:
  247. break;
  248. case self::RAW:
  249. $data = rawurldecode($data);
  250. break;
  251. case self::URL:
  252. $data = urldecode($data);
  253. break;
  254. case self::WDDX:
  255. $data = wddx_deserialize($data);
  256. break;
  257. case self::BZIP:
  258. // $params['small'] = Use bzip2 'small memory' mode?
  259. $data = bzdecompress($data, isset($params['small']) ? $params['small'] : false);
  260. break;
  261. case self::IMAP8:
  262. $data = quoted_printable_decode($data);
  263. break;
  264. case self::IMAPUTF7:
  265. $data = Horde_String::convertCharset(Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($data), 'UTF-8', 'ISO-8859-1');
  266. break;
  267. case self::IMAPUTF8:
  268. $data = Horde_Mime::encode($data);
  269. break;
  270. case self::BASIC:
  271. $data2 = @unserialize($data);
  272. // Unserialize can return false both on error and if $data is the
  273. // false value.
  274. if (($data2 === false) && ($data == serialize(false))) {
  275. return $data2;
  276. }
  277. $data = $data2;
  278. break;
  279. case self::GZ_DEFLATE:
  280. $data = gzinflate($data);
  281. break;
  282. case self::BASE64:
  283. $data = base64_decode($data);
  284. break;
  285. case self::GZ_COMPRESS:
  286. $data = gzuncompress($data);
  287. break;
  288. // $params = Output character set
  289. case self::UTF7:
  290. $data = Horde_String::convertCharset($data, 'utf-7', $params);
  291. break;
  292. // $params = Output character set
  293. case self::UTF7_BASIC:
  294. $data = self::unserialize($data, array(self::BASIC, self::UTF7), $params);
  295. break;
  296. case self::JSON:
  297. $out = json_decode($data);
  298. if (!is_null($out) || (strcasecmp($data, 'null') === 0)) {
  299. return $out;
  300. }
  301. break;
  302. case self::LZF:
  303. $data = @lzf_decompress($data);
  304. break;
  305. }
  306. if ($data === false) {
  307. throw new Horde_Serialize_Exception('Unserialization failed.');
  308. }
  309. return $data;
  310. }
  311. }