spomky-labs/cbor-php
RFC 8949 CBOR encoder/decoder for PHP 8+. Supports all major types, tags (extensible), streaming decode, indefinite-length items, and normalization to native PHP values. Includes common tags and tools for custom tags.
Installation:
composer require spomky-labs/cbor-php
Ensure PHP 8.0+ with ext-mbstring and optionally ext-gmp/ext-bcmath for performance.
First Use Case: Encode a simple PHP array to CBOR and decode it back:
use CBOR\Encoder;
use CBOR\Decoder;
use CBOR\StringStream;
// Encode
$encoder = Encoder::create();
$cbor = $encoder->encode(['name' => 'Alice', 'age' => 30]);
$encoded = (string) $cbor;
// Decode
$decoder = Decoder::create();
$decoded = $decoder->decode(StringStream::create($encoded));
$phpData = $decoded->normalize(); // ['name' => 'Alice', 'age' => 30]
Where to Look First:
Encoder for direct PHP-to-CBOR conversion or build objects manually for fine-grained control.// Direct encoding
$encoder = Encoder::create();
$cbor = $encoder->encode([
'user' => [
'name' => 'Bob',
'scores' => [95, 87],
'timestamp' => time(),
],
]);
// Manual object building (for complex types)
$map = MapObject::create()
->add(TextStringObject::create('key'), TextStringObject::create('value'));
$encoded = (string) $map;
Decoder with StringStream or BinaryStream for memory efficiency.$decoder = Decoder::create();
$decoded = $decoder->decode(StringStream::create($encodedCBOR));
$phpData = $decoded->normalize(); // Convert to native PHP types
StreamingDecoder to process CBOR data in chunks (e.g., for IoT or large files).$stream = new BinaryStream(fopen('large.cbor', 'rb'));
$decoder = new StreamingDecoder();
$decoder->decode($stream, function ($object) {
// Process each CBOR object as it's decoded
$data = $object->normalize();
// Handle $data...
});
TimestampTag, DecimalFractionTag) or create custom tags for domain-specific semantics.// Built-in tag
$timestamp = TimestampTag::create(UnsignedIntegerObject::create(time()));
$dateTime = $timestamp->normalize(); // DateTimeImmutable
// Custom tag (see below)
$email = EmailTag::createFromEmail('user@example.com');
CBOR\Tag for domain-specific needs (e.g., validating email formats, adding metadata).namespace App\CBOR\Tag;
use CBOR\Tag;
use CBOR\TextStringObject;
use InvalidArgumentException;
class DomainTag extends Tag
{
public static function getTagId(): int { return 256; } // Private-use tag
public static function createFromLoadedData(
int $ai,
?string $data,
CBORObject $object
): Tag {
if (!$object instanceof TextStringObject) {
throw new InvalidArgumentException('DomainTag requires a text string');
}
return new self($ai, $data, $object);
}
public function normalize(): string {
return strtolower($this->object->normalize());
}
}
// Store CBOR in Redis
$cbor = Encoder::create()->encode(['key' => 'value']);
Redis::set('cbor_key', (string) $cbor);
// Retrieve and decode
$encoded = Redis::get('cbor_key');
$decoded = Decoder::create()->decode(StringStream::create($encoded))->normalize();
mediumBlob or longBlob columns (MySQL) or bytea (PostgreSQL).doctrine/dbal for type-safe queries:
$cbor = $encoder->encode($model->toArray());
$model->cbor_data = $cbor->getBytes();
return response()->make(
(string) Encoder::create()->encode($data),
200,
['Content-Type' => 'application/cbor']
);
event(new CborEvent((string) Encoder::create()->encode($payload)));
$validator = Validator::make($request->all(), [
'cbor_data' => 'required|cbor', // Custom rule to validate CBOR structure
]);
CBOR\Test\Assert for assertions in PHPUnit:
$this->assertCborEquals(
Encoder::create()->encode(['key' => 'value']),
$decodedObject
);
UnsignedIntegerObject instead of int).->normalize() to convert to native types:
$decoded = $decoder->decode($stream)->normalize();
int (e.g., 64-bit) may overflow.ext-gmp or ext-bcmath for arbitrary-precision integers:
// Enable in composer.json
"config": {
"preferred-install": {
"ext-gmp": "*"
}
}
IndefiniteLengthListObject, IndefiniteLengthMapObject) require streaming or manual handling.StreamingDecoder or ensure the object is fully loaded before normalization:
$decoder = Decoder::create();
$object = $decoder->decode($stream);
if ($object instanceof IndefiniteLengthListObject) {
$object->finalize(); // Ensure all items are loaded
}
public function __construct(int $ai, ?string $data, CBORObject $object) {
if (!$object instanceof TextStringObject) {
throw new InvalidArgumentException('Expected TextStringObject');
}
parent::__construct($ai, $data, $object);
}
StreamingDecoder callbacks may receive partial objects.IndefiniteLength objects and handle finalization:
$decoder->decode($stream, function ($object) {
if ($object instanceof IndefiniteLengthObject) {
$object->finalize(); // Ensure completeness
}
$data = $object->normalize();
// Process $data
});
StreamingDecoder or chunk the input:
$stream
How can I help you explore Laravel packages today?