* @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'); } }