* @category Horde * @copyright 2013-2016 Horde LLC * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 * @package Pack */ class Horde_Pack { /* Default compress length (in bytes). */ const DEFAULT_COMPRESS = 128; /* Mask for compressed data. */ const COMPRESS_MASK = 64; /** * Instance of Horde_Compress_Fast shared between all instances. * * @var Horde_Compress_Fast */ protected static $_compress; /** * Drivers. Shared between all instances. * * @var array */ protected static $_drivers = array(); /** * Constructor. */ public function __construct() { if (empty(self::$_drivers)) { $fi = new FilesystemIterator(__DIR__ . '/Pack/Driver'); $class_prefix = __CLASS__ . '_Driver_'; foreach ($fi as $val) { if ($val->isFile()) { $cname = $class_prefix . $val->getBasename('.php'); if (class_exists($cname) && $cname::supported()) { $ob = new $cname(); self::$_drivers[$ob->id] = $ob; } } } krsort(self::$_drivers, SORT_NUMERIC); self::$_compress = new Horde_Compress_Fast(); } } /** */ public function __sleep() { throw new LogicException('Object can not be serialized.'); } /** * Pack a string. * * @param mixed $data The data to pack. * @param array $opts Additional options: *
* - compress: (mixed) If false, don't use compression. If true, uses
* default compress length (DEFAULT). If 0, always compress.
* All other integer values: compress only if data is
* greater than this string length.
* - drivers: (array) Only use these drivers to pack. By default, driver
* to use is auto-determined.
* - phpob: (boolean) If true, the data contains PHP serializable
* objects (i.e. objects that have a PHP-specific serialized
* representation). If false, the data does not contain any of
* these objects. If not present, will auto-determine
* existence of these objects.
*
*
* @return string The packed string.
* @throws Horde_Pack_Exception
*/
public function pack($data, array $opts = array())
{
$opts = array_merge(array(
'compress' => true
), $opts);
if (!isset($opts['phpob'])) {
$auto = new Horde_Pack_Autodetermine($data);
$opts['phpob'] = $auto->phpob;
}
foreach (self::$_drivers as $key => $val) {
if (!empty($opts['phpob']) && !$val->phpob) {
continue;
}
if (isset($opts['drivers'])) {
if (!in_array(get_class($val), $opts['drivers'])) {
continue;
}
}
/* Format of data:
* First-byte:
* 1,2,4,8,16,32 - Reserved for pack format.
* 64 - Packed data has been compressed.
* 128 - RESERVED for future use (if set, indicates that initial
* byte will extend into next byte).
* Packed (and compressed data) follows this byte. */
try {
$packed = $val->pack($data);
} catch (Horde_Pack_Exception $e) {
continue;
}
if ($opts['compress'] !== false) {
if ($opts['compress'] === 0) {
$compress = true;
} else {
if ($opts['compress'] === true) {
$opts['compress'] = self::DEFAULT_COMPRESS;
}
$compress = strlen($packed) > $opts['compress'];
}
if ($compress) {
$packed = self::$_compress->compress($packed);
$key |= self::COMPRESS_MASK;
}
}
return pack('C', $key) . $packed;
}
throw new Horde_Pack_Exception('Could not pack data.');
}
/**
* Unpack a string.
*
* @param string $data The packed string.
*
* @return mixed The unpacked data.
* @throws Horde_Pack_Exception
*/
public function unpack($data)
{
if (!$data) {
return $data;
}
if (is_string($data)) {
$mask = unpack('C*', $data[0]);
$mask = reset($mask);
$data = substr($data, 1);
if ($mask & self::COMPRESS_MASK) {
$data = self::$_compress->decompress($data);
$mask ^= self::COMPRESS_MASK;
}
if (isset(self::$_drivers[$mask])) {
try {
return self::$_drivers[$mask]->unpack($data);
} catch (Horde_Pack_Exception $e) {
throw $e;
} catch (Exception $e) {
/* Unknown exceptions are handled with the throw below. */
}
}
}
throw new Horde_Pack_Exception('Could not unpack data');
}
}