spomky-labs/cbor-php
RFC 8949 CBOR encoder/decoder for PHP 8+. Supports all major types, tags, indefinite-length items, streaming decoding, and normalization to native PHP types. Extensible tag system with built-in common tags and modern, type-safe API.
Installation:
composer require spomky-labs/cbor-php
Ensure ext-mbstring and brick/math are installed (GMP/BCMath recommended for performance).
First Use Case: Encode a simple PHP array to CBOR and decode it back:
use CBOR\Encoder;
use CBOR\Decoder;
// Encode
$encoder = Encoder::create();
$encoded = $encoder->encode(['name' => 'John', 'age' => 30]);
// Decode
$decoder = Decoder::create();
$decoded = $decoder->decode($encoded);
$phpData = $decoded->normalize(); // ['name' => 'John', 'age' => 30]
Where to Look First:
Encoder::create() to convert native PHP types (arrays, strings, numbers) to CBOR binary:
$encoder = Encoder::create();
$cborData = $encoder->encode([
'user' => [
'name' => 'Alice',
'metadata' => ['role' => 'admin', 'active' => true]
],
'timestamp' => time()
]);
MapObject, ListObject) for finer control over CBOR types.Decoder::create() with a StringStream or FileStream for large data:
$decoder = Decoder::create();
$decoded = $decoder->decode(StringStream::create($cborBinary));
$phpData = $decoded->normalize(); // Convert to native types
StreamingDecoder to avoid memory overload:
$stream = new FileStream('large.cbor');
$decoder = Decoder\StreamingDecoder::create();
foreach ($decoder->decode($stream) as $item) {
// Process each CBOR item incrementally
}
use CBOR\Tag\TimestampTag;
use CBOR\UnsignedIntegerObject;
$timestamp = TimestampTag::create(UnsignedIntegerObject::create(time()));
$normalized = $timestamp->normalize(); // DateTimeImmutable
Tag\AbstractTag for proprietary formats (see Custom Tags Guide).MapObject for key-value pairs:
$map = MapObject::create()
->add(TextStringObject::create('key'), TextStringObject::create('value'));
ListObject for ordered sequences:
$list = ListObject::create([
UnsignedIntegerObject::create(1),
TextStringObject::create('item')
]);
IndefiniteLengthMapObject or IndefiniteLengthListObject for streaming or unknown-size data.// In a Laravel controller
public function store(Request $request) {
$decoder = Decoder::create();
$data = $decoder->decode($request->getContent())->normalize();
// Process $data...
}
$cborData = Encoder::create()->encode($data);
Cache::put('key', $cborData, $ttl);
use CBOR\Attribute;
#[Attribute]
public function getCborAttribute(): string {
return (string) Encoder::create()->encode($this->attribute);
}
use CBOR\Tag\COSE\Sign1Tag;
$signature = Sign1Tag::create(
UnsignedIntegerObject::create(1),
ByteStringObject::create($protectedHeader),
ByteStringObject::create($signatureData)
);
$decoder = Decoder::create();
$authData = $decoder->decode($request->input('authData'))->normalize();
Type Mismatches:
UnsignedIntegerObject) and negative integers (NegativeIntegerObject). PHP’s int may not preserve this distinction during normalization.Floating-Point Precision:
Float64Object and Float32Object may lose precision for very large/small numbers. Use DecimalFractionTag for financial/precision-critical data.$decimal = DecimalFractionTag::createFromFloat(3.14159);
$normalized = (string) $decimal->normalize(); // "3.14159"
Indefinite-Length Objects:
IndefiniteLengthMapObject/IndefiniteLengthListObject require streaming or known termination. Forgetting to close them causes decoding errors.IndefiniteLengthMapObject::create()->break() to terminate.Tag Conflicts:
Tag\TimestampTag uses ID 0) will override them. Always check Tags Reference before defining new tags.2^32 for custom tags to avoid collisions.Large Integers:
ext-gmp or ext-bcmath, integers > PHP_INT_MAX may fail or lose precision. Use brick/math (included as a dependency) for arbitrary-precision arithmetic.php.ini for better performance:
extension=gmp
extension=bcmath
Streaming Decoder Quirks:
StreamingDecoder processes one CBOR item at a time. Nested structures (e.g., maps containing lists) must be fully decoded before normalization.Decoder::create()->decode() for nested structures, then stream individual top-level items.Normalization Edge Cases:
normalize() converts CBOR types to PHP natives, but some types (e.g., ByteStringObject) become string. For binary data, use base64_encode() or hex2bin():
$binaryData = hex2bin($decoded->normalize()['binary_key']);
Validate CBOR:
Use CBOR\Validator to check binary data before decoding:
if (!Validator::validate($cborBinary)) {
throw new \RuntimeException('Invalid CBOR data');
}
Inspect Raw CBOR: Convert CBOR to a readable format for debugging:
$decoded = Decoder::create()->decode($cborBinary);
$debugInfo = $decoded->toArray(); // Recursive structure
Enable Strict Typing:
Use PHP 8’s declare(strict_types=1) and type hints to catch CBOR-PHP type mismatches early.
Performance Profiling:
Decoder::create() vs. StreamingDecoder using memory_get_usage().brick/math is required but may throw warnings if ext-gmp/ext-bcmath are missing. Suppress warnings or install the extensions.How can I help you explore Laravel packages today?