spomky-labs/cbor-php
Comprehensive PHP 8+ CBOR (RFC 8949) encoder/decoder with full major type support, extensible tags, streaming decoding, indefinite-length handling, and normalization to native PHP types; includes common tags and custom tag support.
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;
$data = ['name' => 'John', 'age' => 30];
$encoder = Encoder::create();
$encoded = $encoder->encode($data); // Binary string
$decoder = Decoder::create();
$decoded = $decoder->decode(StringStream::create($encoded));
$phpData = $decoded->normalize(); // ['name' => 'John', 'age' => 30]
Key Classes to Know:
Encoder: Converts PHP arrays/objects to CBOR.Decoder: Parses CBOR into CBORObject instances.StringStream: Wraps binary data for decoding.MapObject, ListObject: Core CBOR data structures.Use Encoder for seamless conversion from native PHP types:
$encoder = Encoder::create();
$encoded = $encoder->encode([
'user' => [
'name' => 'Alice',
'scores' => [95, 87],
'metadata' => ['active' => true, 'tags' => ['admin', 'premium']]
]
]);
UnsignedIntegerObject, TextStringObject, etc., for explicit control.use CBOR\Tag\TimestampTag;
$timestamp = TimestampTag::create(UnsignedIntegerObject::create(time()));
Use Decoder with StringStream for binary data:
$decoder = Decoder::create();
$decoded = $decoder->decode(StringStream::create($binaryData));
$data = $decoded->normalize(); // Convert to PHP types
For large payloads (e.g., IoT telemetry), use StreamingDecoder:
$stream = fopen('large.cbor', 'r');
$decoder = Decoder::create();
foreach ($decoder->decodeStream($stream) as $item) {
$phpItem = $item->normalize();
// Process item
}
Extend Tag for domain-specific needs (e.g., WebAuthn, COSE):
namespace App\CBOR\Tag;
use CBOR\Tag;
use CBOR\CBORObject;
class WebAuthnCredentialTag extends Tag
{
public static function getTagId(): int { return 1801; } // IANA-registered
public static function createFromLoadedData(
int $ai,
?string $data,
CBORObject $object
): Tag {
return new self($ai, $data, $object);
}
public function normalize(): array {
return [
'type' => 'webauthn',
'data' => $this->object->normalize()
];
}
}
$credential = WebAuthnCredentialTag::create($cborObject);
$encoded = (string) $credential;
use Illuminate\Http\Request;
use CBOR\Decoder;
public function handle(Request $request)
{
$decoder = Decoder::create();
$data = $decoder->decode($request->getContent())->normalize();
// Process $data
}
use Illuminate\Database\Eloquent\Casts\Attribute;
public function cborData(): Attribute
{
return Attribute::make(
get: fn ($value) => $value ? Decoder::create()->decode($value)->normalize() : null,
set: fn ($value) => Encoder::create()->encode($value)
);
}
Type Mismatches:
UnsignedIntegerObject/NegativeIntegerObject explicitly.$encoder->encode(['age' => (int) $age]); // Ensure integer type
Tag Collisions:
Streaming Decoder Quirks:
StreamingDecoder emits CBORObject instances sequentially. Ensure your loop handles partial objects.if (!$item->isComplete()) continue;
Large Integers:
ext-gmp/ext-bcmath, large integers (>64-bit) may lose precision.BigIntegerObject for arbitrary-precision arithmetic.Inspect Raw CBOR:
Use bin2hex() to debug binary data:
$hex = bin2hex($encoded);
echo "Encoded: " . substr($hex, 0, 20) . "...";
Validate Decoded Data: Compare normalized output with original input:
$normalized = $decoded->normalize();
assert($normalized === $originalData);
Tag Debugging: Check tag IDs during decoding:
$decoder->onTag(function (int $tagId, CBORObject $object) {
error_log("Tag $tagId: " . $object->getType());
});
Custom Encoders/Decoders:
Implement EncoderInterface/DecoderInterface for protocol-specific handling (e.g., COSE):
class COSEEncoder implements EncoderInterface
{
public function encode($data): string
{
// Custom COSE encoding logic
}
}
Normalization Hooks:
Override normalize() in custom objects/tags for type conversion:
class CustomObject extends CBORObject
{
public function normalize(): array
{
return ['custom' => $this->getValue()];
}
}
Performance Optimization:
Encoder/Decoder instances (they are stateless).$encoder = Encoder::create();
$batch = array_map([$encoder, 'encode'], $dataArray);
Streaming Decoder Buffers:
$decoder = Decoder::create()->withBufferSize(16384); // 16KB
Tag Resolution:
$decoder = Decoder::create();
$decoder->addTagResolver(1234, function ($ai, $data, $object) {
return new CustomTag($ai, $data, $object);
});
Indefinite-Length Objects:
IndefiniteLengthMapObject/IndefiniteLengthListObject for streaming data.break in streaming decoder).How can I help you explore Laravel packages today?