Update website

This commit is contained in:
Guilhem Lavaux 2024-11-23 20:45:29 +01:00
parent 41ce1aa076
commit ea0eb1c6e0
4222 changed files with 721797 additions and 14 deletions

View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014-2018 Spomky-Labs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,35 @@
{
"name": "spomky-labs/base64url",
"description": "Base 64 URL Safe Encoding/Decoding PHP Library",
"type": "library",
"license": "MIT",
"keywords": ["Base64", "URL", "Safe", "RFC4648"],
"homepage": "https://github.com/Spomky-Labs/base64url",
"authors": [
{
"name": "Florent Morselli",
"homepage": "https://github.com/Spomky-Labs/base64url/contributors"
}
],
"autoload": {
"psr-4": {
"Base64Url\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Base64Url\\Test\\": "tests/"
}
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.11|^0.12",
"phpstan/phpstan-beberlei-assert": "^0.11|^0.12",
"phpstan/phpstan-deprecation-rules": "^0.11|^0.12",
"phpstan/phpstan-phpunit": "^0.11|^0.12",
"phpstan/phpstan-strict-rules": "^0.11|^0.12"
}
}

View file

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Base64Url;
use function base64_decode;
use function base64_encode;
use InvalidArgumentException;
use function rtrim;
use function strtr;
/**
* Encode and decode data into Base64 Url Safe.
*/
final class Base64Url
{
/**
* @param string $data The data to encode
* @param bool $usePadding If true, the "=" padding at end of the encoded value are kept, else it is removed
*
* @return string The data encoded
*/
public static function encode(string $data, bool $usePadding = false): string
{
$encoded = strtr(base64_encode($data), '+/', '-_');
return true === $usePadding ? $encoded : rtrim($encoded, '=');
}
/**
* @param string $data The data to decode
*
* @throws InvalidArgumentException
*
* @return string The data decoded
*/
public static function decode(string $data): string
{
$decoded = base64_decode(strtr($data, '-_', '+/'), true);
if (false === $decoded) {
throw new InvalidArgumentException('Invalid data provided');
}
return $decoded;
}
}

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Spomky-Labs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,45 @@
{
"name": "spomky-labs/cbor-php",
"type": "library",
"license": "MIT",
"keywords": ["CBOR", "Concise Binary Object Representation", "RFC7049"],
"description": "CBOR Encoder/Decoder for PHP",
"authors": [
{
"name": "Florent Morselli",
"homepage": "https://github.com/Spomky"
},{
"name": "All contributors",
"homepage": "https://github.com/Spomky-Labs/cbor-php/contributors"
}
],
"autoload": {
"psr-4": {
"CBOR\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"CBOR\\Test\\": "tests/"
}
},
"require": {
"php": ">=7.1",
"spomky-labs/base64url": "^1.0|^2.0",
"beberlei/assert": "^3.2",
"brick/math": "^0.8.15"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.0",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-beberlei-assert": "^0.12",
"phpstan/phpstan-deprecation-rules": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpstan/phpstan-strict-rules": "^0.12",
"phpunit/phpunit": "^7.5|^8.0"
},
"suggest": {
"ext-gmp": "GMP or BCMath extensions will drastically improve the library performance",
"ext-bcmath": "GMP or BCMath extensions will drastically improve the library performance. BCMath extension needed to handle the Big Float and Decimal Fraction Tags"
}
}

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use function chr;
abstract class AbstractCBORObject implements CBORObject
{
/**
* @var int
*/
protected $additionalInformation;
/**
* @var int
*/
private $majorType;
public function __construct(int $majorType, int $additionalInformation)
{
$this->majorType = $majorType;
$this->additionalInformation = $additionalInformation;
}
public function __toString(): string
{
return chr($this->majorType << 5 | $this->additionalInformation);
}
public function getMajorType(): int
{
return $this->majorType;
}
public function getAdditionalInformation(): int
{
return $this->additionalInformation;
}
}

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
final class ByteStringObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b010;
/**
* @var string
*/
private $value;
/**
* @var int|null
*/
private $length;
public function __construct(string $data)
{
list($additionalInformation, $length) = LengthCalculator::getLengthOfString($data);
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->length = $length;
$this->value = $data;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->length) {
$result .= $this->length;
}
$result .= $this->value;
return $result;
}
public function getValue(): string
{
return $this->value;
}
public function getLength(): int
{
return mb_strlen($this->value, '8bit');
}
public function getNormalizedData(bool $ignoreTags = false): string
{
return $this->value;
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use InvalidArgumentException;
final class ByteStringWithChunkObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b010;
private const ADDITIONAL_INFORMATION = 0b00011111;
/**
* @var ByteStringObject[]
*/
private $chunks = [];
public function __construct()
{
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
}
public function __toString(): string
{
$result = parent::__toString();
foreach ($this->chunks as $chunk) {
$result .= (string) $chunk;
}
$bin = hex2bin('FF');
if (false === $bin) {
throw new InvalidArgumentException('Unable to convert the data');
}
$result .= $bin;
return $result;
}
public function add(ByteStringObject $chunk): void
{
$this->chunks[] = $chunk;
}
public function append(string $chunk): void
{
$this->add(new ByteStringObject($chunk));
}
public function getValue(): string
{
$result = '';
foreach ($this->chunks as $chunk) {
$result .= $chunk->getValue();
}
return $result;
}
public function getLength(): int
{
$length = 0;
foreach ($this->chunks as $chunk) {
$length += $chunk->getLength();
}
return $length;
}
public function getNormalizedData(bool $ignoreTags = false): string
{
$result = '';
foreach ($this->chunks as $chunk) {
$result .= $chunk->getNormalizedData($ignoreTags);
}
return $result;
}
}

View file

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
interface CBORObject
{
public function __toString(): string;
public function getMajorType(): int;
public function getAdditionalInformation(): int;
/**
* @return mixed|null
*/
public function getNormalizedData(bool $ignoreTags = false);
}

View file

@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use CBOR\OtherObject\BreakObject;
use CBOR\OtherObject\OtherObjectManager;
use CBOR\Tag\TagObjectManager;
use InvalidArgumentException;
use function ord;
use RuntimeException;
final class Decoder
{
/**
* @var TagObjectManager
*/
private $tagObjectManager;
/**
* @var OtherObjectManager
*/
private $otherTypeManager;
public function __construct(TagObjectManager $tagObjectManager, OtherObjectManager $otherTypeManager)
{
$this->tagObjectManager = $tagObjectManager;
$this->otherTypeManager = $otherTypeManager;
}
public function decode(Stream $stream): CBORObject
{
return $this->process($stream);
}
private function process(Stream $stream, bool $breakable = false): CBORObject
{
$ib = ord($stream->read(1));
$mt = $ib >> 5;
$ai = $ib & 0b00011111;
$val = null;
switch ($ai) {
case 0b00011000: //24
case 0b00011001: //25
case 0b00011010: //26
case 0b00011011: //27
$val = $stream->read(2 ** ($ai & 0b00000111));
break;
case 0b00011100: //28
case 0b00011101: //29
case 0b00011110: //30
throw new InvalidArgumentException(sprintf('Cannot parse the data. Found invalid Additional Information "%s" (%d).', str_pad(decbin($ai), 5, '0', STR_PAD_LEFT), $ai));
case 0b00011111: //31
return $this->processInfinite($stream, $mt, $breakable);
}
return $this->processFinite($stream, $mt, $ai, $val);
}
private function processFinite(Stream $stream, int $mt, int $ai, ?string $val): CBORObject
{
switch ($mt) {
case 0b000: //0
return UnsignedIntegerObject::createObjectForValue($ai, $val);
case 0b001: //1
return SignedIntegerObject::createObjectForValue($ai, $val);
case 0b010: //2
$length = null === $val ? $ai : Utils::binToInt($val);
return new ByteStringObject($stream->read($length));
case 0b011: //3
$length = null === $val ? $ai : Utils::binToInt($val);
return new TextStringObject($stream->read($length));
case 0b100: //4
$object = new ListObject();
$nbItems = null === $val ? $ai : Utils::binToInt($val);
for ($i = 0; $i < $nbItems; ++$i) {
$object->add($this->process($stream));
}
return $object;
case 0b101: //5
$object = new MapObject();
$nbItems = null === $val ? $ai : Utils::binToInt($val);
for ($i = 0; $i < $nbItems; ++$i) {
$object->add($this->process($stream), $this->process($stream));
}
return $object;
case 0b110: //6
return $this->tagObjectManager->createObjectForValue($ai, $val, $this->process($stream));
case 0b111: //7
return $this->otherTypeManager->createObjectForValue($ai, $val);
default:
throw new RuntimeException(sprintf('Unsupported major type "%s" (%d).', str_pad(decbin($mt), 5, '0', STR_PAD_LEFT), $mt)); // Should never append
}
}
private function processInfinite(Stream $stream, int $mt, bool $breakable): CBORObject
{
switch ($mt) {
case 0b010: //2
$object = new ByteStringWithChunkObject();
while (!($it = $this->process($stream, true)) instanceof BreakObject) {
if (!$it instanceof ByteStringObject) {
throw new RuntimeException('Unable to parse the data. Infinite Byte String object can only get Byte String objects.');
}
$object->add($it);
}
return $object;
case 0b011: //3
$object = new TextStringWithChunkObject();
while (!($it = $this->process($stream, true)) instanceof BreakObject) {
if (!$it instanceof TextStringObject) {
throw new RuntimeException('Unable to parse the data. Infinite Text String object can only get Text String objects.');
}
$object->add($it);
}
return $object;
case 0b100: //4
$object = new InfiniteListObject();
while (!($it = $this->process($stream, true)) instanceof BreakObject) {
$object->add($it);
}
return $object;
case 0b101: //5
$object = new InfiniteMapObject();
while (!($it = $this->process($stream, true)) instanceof BreakObject) {
$object->append($it, $this->process($stream));
}
return $object;
case 0b111: //7
if (!$breakable) {
throw new InvalidArgumentException('Cannot parse the data. No enclosing indefinite.');
}
return new BreakObject();
case 0b000: //0
case 0b001: //1
case 0b110: //6
default:
throw new InvalidArgumentException(sprintf('Cannot parse the data. Found infinite length for Major Type "%s" (%d).', str_pad(decbin($mt), 5, '0', STR_PAD_LEFT), $mt));
}
}
}

View file

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use ArrayIterator;
use function count;
use Countable;
use InvalidArgumentException;
use Iterator;
use IteratorAggregate;
final class InfiniteListObject extends AbstractCBORObject implements Countable, IteratorAggregate
{
private const MAJOR_TYPE = 0b100;
private const ADDITIONAL_INFORMATION = 0b00011111;
/**
* @var CBORObject[]
*/
private $data = [];
public function __construct()
{
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
}
public function __toString(): string
{
$result = parent::__toString();
foreach ($this->data as $object) {
$result .= (string) $object;
}
$bin = hex2bin('FF');
if (false === $bin) {
throw new InvalidArgumentException('Unable to convert the data');
}
$result .= $bin;
return $result;
}
public function getNormalizedData(bool $ignoreTags = false): array
{
return array_map(function (CBORObject $item) use ($ignoreTags) {
return $item->getNormalizedData($ignoreTags);
}, $this->data);
}
public function add(CBORObject $item): void
{
$this->data[] = $item;
}
public function count(): int
{
return count($this->data);
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->data);
}
}

View file

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use ArrayIterator;
use function count;
use Countable;
use InvalidArgumentException;
use Iterator;
use IteratorAggregate;
final class InfiniteMapObject extends AbstractCBORObject implements Countable, IteratorAggregate
{
private const MAJOR_TYPE = 0b101;
private const ADDITIONAL_INFORMATION = 0b00011111;
/**
* @var MapItem[]
*/
private $data = [];
public function __construct()
{
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
}
public function __toString(): string
{
$result = parent::__toString();
foreach ($this->data as $object) {
$result .= (string) $object->getKey();
$result .= (string) $object->getValue();
}
$bin = hex2bin('FF');
if (false === $bin) {
throw new InvalidArgumentException('Unable to convert the data');
}
$result .= $bin;
return $result;
}
public function append(CBORObject $key, CBORObject $value): void
{
$this->data[] = new MapItem($key, $value);
}
public function count(): int
{
return count($this->data);
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->data);
}
public function getNormalizedData(bool $ignoreTags = false): array
{
$result = [];
foreach ($this->data as $object) {
$result[$object->getKey()->getNormalizedData($ignoreTags)] = $object->getValue()->getNormalizedData($ignoreTags);
}
return $result;
}
}

View file

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use Brick\Math\BigInteger;
use function chr;
use function count;
use InvalidArgumentException;
final class LengthCalculator
{
public static function getLengthOfString(string $data): array
{
$length = mb_strlen($data, '8bit');
return self::computeLength($length);
}
public static function getLengthOfArray(array $data): array
{
$length = count($data);
return self::computeLength($length);
}
private static function computeLength(int $length): array
{
switch (true) {
case $length < 24:
return [$length, null];
case $length < 0xFF:
return [24, chr($length)];
case $length < 0xFFFF:
return [25, self::hex2bin(static::fixHexLength(Utils::intToHex($length)))];
case $length < 0xFFFFFFFF:
return [26, self::hex2bin(static::fixHexLength(Utils::intToHex($length)))];
case BigInteger::of($length)->isLessThan(BigInteger::fromBase('FFFFFFFFFFFFFFFF', 16)):
return [27, self::hex2bin(static::fixHexLength(Utils::intToHex($length)))];
default:
return [31, null];
}
}
private static function hex2bin(string $data): string
{
$result = hex2bin($data);
if (false === $result) {
throw new InvalidArgumentException('Unable to convert the data');
}
return $result;
}
private static function fixHexLength(string $data): string
{
return str_pad($data, (int) (2 ** ceil(log(mb_strlen($data, '8bit'), 2))), '0', STR_PAD_LEFT);
}
}

View file

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use function array_key_exists;
use ArrayIterator;
use function count;
use Countable;
use InvalidArgumentException;
use Iterator;
use IteratorAggregate;
class ListObject extends AbstractCBORObject implements Countable, IteratorAggregate
{
private const MAJOR_TYPE = 0b100;
/**
* @var CBORObject[]
*/
private $data = [];
/**
* @var int|null
*/
private $length;
/**
* @param CBORObject[] $data
*/
public function __construct(array $data = [])
{
list($additionalInformation, $length) = LengthCalculator::getLengthOfArray($data);
array_map(static function ($item): void {
if (!$item instanceof CBORObject) {
throw new InvalidArgumentException('The list must contain only CBORObject objects.');
}
}, $data);
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
$this->length = $length;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->length) {
$result .= $this->length;
}
foreach ($this->data as $object) {
$result .= (string) $object;
}
return $result;
}
public function add(CBORObject $object): void
{
$this->data[] = $object;
list($this->additionalInformation, $this->length) = LengthCalculator::getLengthOfArray($this->data);
}
public function get(int $index): CBORObject
{
if (!array_key_exists($index, $this->data)) {
throw new InvalidArgumentException('Index not found.');
}
return $this->data[$index];
}
public function getNormalizedData(bool $ignoreTags = false): array
{
return array_map(function (CBORObject $item) use ($ignoreTags) {
return $item->getNormalizedData($ignoreTags);
}, $this->data);
}
public function count(): int
{
return count($this->data);
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->data);
}
}

View file

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
class MapItem
{
/**
* @var CBORObject
*/
private $key;
/**
* @var CBORObject
*/
private $value;
public function __construct(CBORObject $key, CBORObject $value)
{
$this->key = $key;
$this->value = $value;
}
public function getKey(): CBORObject
{
return $this->key;
}
public function getValue(): CBORObject
{
return $this->value;
}
}

View file

@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use ArrayIterator;
use function count;
use Countable;
use InvalidArgumentException;
use Iterator;
use IteratorAggregate;
final class MapObject extends AbstractCBORObject implements Countable, IteratorAggregate
{
private const MAJOR_TYPE = 0b101;
/**
* @var MapItem[]
*/
private $data = [];
/**
* @var int|null
*/
private $length;
/**
* @param MapItem[] $data
*/
public function __construct(array $data = [])
{
list($additionalInformation, $length) = LengthCalculator::getLengthOfArray($data);
array_map(static function ($item): void {
if (!$item instanceof MapItem) {
throw new InvalidArgumentException('The list must contain only MapItem objects.');
}
}, $data);
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
$this->length = $length;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->length) {
$result .= $this->length;
}
foreach ($this->data as $object) {
$result .= (string) $object->getKey();
$result .= (string) $object->getValue();
}
return $result;
}
public function add(CBORObject $key, CBORObject $value): void
{
$this->data[] = new MapItem($key, $value);
list($this->additionalInformation, $this->length) = LengthCalculator::getLengthOfArray($this->data);
}
public function count(): int
{
return count($this->data);
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->data);
}
public function getNormalizedData(bool $ignoreTags = false): array
{
$result = [];
foreach ($this->data as $object) {
$result[$object->getKey()->getNormalizedData($ignoreTags)] = $object->getValue()->getNormalizedData($ignoreTags);
}
return $result;
}
}

View file

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
abstract class OtherObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b111;
/**
* @var string|null
*/
protected $data;
public function __construct(int $additionalInformation, ?string $data)
{
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->data) {
$result .= $this->data;
}
return $result;
}
/**
* @return int[]
*/
abstract public static function supportedAdditionalInformation(): array;
abstract public static function createFromLoadedData(int $additionalInformation, ?string $data): self;
}

View file

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class BreakObject extends Base
{
public function __construct()
{
parent::__construct(0b00011111, null);
}
public static function supportedAdditionalInformation(): array
{
return [0b00011111];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false): bool
{
return false;
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use Assert\Assertion;
use Brick\Math\BigInteger;
use CBOR\OtherObject as Base;
use CBOR\Utils;
use InvalidArgumentException;
final class DoublePrecisionFloatObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [27];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
/**
* @return DoublePrecisionFloatObject
*/
public static function create(string $value): self
{
if (8 !== mb_strlen($value, '8bit')) {
throw new InvalidArgumentException('The value is not a valid double precision floating point');
}
return new self(27, $value);
}
public function getNormalizedData(bool $ignoreTags = false)
{
$exp = $this->getExponent();
$mant = $this->getMantissa();
$sign = $this->getSign();
if (0 === $exp) {
$val = $mant * 2 ** (-(1022 + 52));
} elseif (0b11111111111 !== $exp) {
$val = ($mant + (1 << 52)) * 2 ** ($exp - (1023 + 52));
} else {
$val = 0 === $mant ? INF : NAN;
}
return $sign * $val;
}
public function getExponent(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->shiftedRight(52)->and(Utils::hexToBigInteger('7ff'))->toInt();
}
public function getMantissa(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('fffffffffffff'))->toInt();
}
public function getSign(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
$sign = Utils::binToBigInteger($data)->shiftedRight(63);
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
}
}

View file

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class FalseObject extends Base
{
public function __construct()
{
parent::__construct(20, null);
}
public static function supportedAdditionalInformation(): array
{
return [20];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false): bool
{
return false;
}
}

View file

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class GenericObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
public function getNormalizedData(bool $ignoreTags = false)
{
return $this->data;
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use Assert\Assertion;
use Brick\Math\BigInteger;
use CBOR\OtherObject as Base;
use CBOR\Utils;
use InvalidArgumentException;
final class HalfPrecisionFloatObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [25];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
/**
* @return HalfPrecisionFloatObject
*/
public static function create(string $value): self
{
if (4 !== mb_strlen($value, '8bit')) {
throw new InvalidArgumentException('The value is not a valid half precision floating point');
}
return new self(25, $value);
}
public function getNormalizedData(bool $ignoreTags = false)
{
$exp = $this->getExponent();
$mant = $this->getMantissa();
$sign = $this->getSign();
if (0 === $exp) {
$val = $mant * 2 ** (-24);
} elseif (0b11111 !== $exp) {
$val = ($mant + (1 << 10)) * 2 ** ($exp - 25);
} else {
$val = 0 === $mant ? INF : NAN;
}
return $sign * $val;
}
public function getExponent(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->shiftedRight(10)->and(Utils::hexToBigInteger('1f'))->toInt();
}
public function getMantissa(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('3ff'))->toInt();
}
public function getSign(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
$sign = Utils::binToBigInteger($data)->shiftedRight(15);
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
}
}

View file

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class NullObject extends Base
{
public function __construct()
{
parent::__construct(22, null);
}
public static function supportedAdditionalInformation(): array
{
return [22];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false)
{
}
}

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use function array_key_exists;
use CBOR\OtherObject;
use InvalidArgumentException;
class OtherObjectManager
{
/**
* @var string[]
*/
private $classes = [];
public function add(string $class): void
{
foreach ($class::supportedAdditionalInformation() as $ai) {
if ($ai < 0) {
throw new InvalidArgumentException('Invalid additional information.');
}
$this->classes[$ai] = $class;
}
}
public function getClassForValue(int $value): string
{
return array_key_exists($value, $this->classes) ? $this->classes[$value] : GenericObject::class;
}
public function createObjectForValue(int $value, ?string $data): OtherObject
{
/** @var OtherObject $class */
$class = $this->getClassForValue($value);
return $class::createFromLoadedData($value, $data);
}
}

View file

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
use CBOR\Utils;
use function chr;
use InvalidArgumentException;
final class SimpleObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 24];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if (null === $this->data) {
return $this->getAdditionalInformation();
}
return Utils::binToInt($this->data);
}
/**
* @return SimpleObject
*/
public static function create(int $value): self
{
switch (true) {
case $value < 24:
return new self($value, null);
case $value < 256:
return new self(24, chr($value));
default:
throw new InvalidArgumentException('The value is not a valid simple value');
}
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use Assert\Assertion;
use Brick\Math\BigInteger;
use CBOR\OtherObject as Base;
use CBOR\Utils;
use InvalidArgumentException;
final class SinglePrecisionFloatObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [26];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
/**
* @return SinglePrecisionFloatObject
*/
public static function create(string $value): self
{
if (4 !== mb_strlen($value, '8bit')) {
throw new InvalidArgumentException('The value is not a valid single precision floating point');
}
return new self(26, $value);
}
public function getNormalizedData(bool $ignoreTags = false)
{
$exp = $this->getExponent();
$mant = $this->getMantissa();
$sign = $this->getSign();
if (0 === $exp) {
$val = $mant * 2 ** (-(126 + 23));
} elseif (0b11111111 !== $exp) {
$val = ($mant + (1 << 23)) * 2 ** ($exp - (127 + 23));
} else {
$val = 0 === $mant ? INF : NAN;
}
return $sign * $val;
}
public function getExponent(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->shiftedRight(23)->and(Utils::hexToBigInteger('ff'))->toInt();
}
public function getMantissa(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('7fffff'))->toInt();
}
public function getSign(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
$sign = Utils::binToBigInteger($data)->shiftedRight(32);
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
}
}

View file

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class TrueObject extends Base
{
public function __construct()
{
parent::__construct(21, null);
}
public static function supportedAdditionalInformation(): array
{
return [21];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false): bool
{
return true;
}
}

View file

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class UndefinedObject extends Base
{
public function __construct()
{
parent::__construct(23, null);
}
public static function supportedAdditionalInformation(): array
{
return [23];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false)
{
return 'undefined';
}
}

View file

@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use Brick\Math\BigInteger;
use GMP;
use InvalidArgumentException;
final class SignedIntegerObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b001;
/**
* @var string|null
*/
private $data;
public function __construct(int $additionalInformation, ?string $data)
{
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->data) {
$result .= $this->data;
}
return $result;
}
public static function createObjectForValue(int $additionalInformation, ?string $data): self
{
return new self($additionalInformation, $data);
}
public static function create(int $value): self
{
return self::createFromString((string) $value);
}
public static function createFromString(string $value): self
{
$integer = BigInteger::of($value);
return self::createBigInteger($integer);
}
/**
* @deprecated Deprecated since v1.1 and will be removed in v2.0. Please use "create" or "createFromString" instead
*/
public static function createFromGmpValue(GMP $value): self
{
if (gmp_cmp($value, gmp_init(0)) >= 0) {
throw new InvalidArgumentException('The value must be a negative integer.');
}
$minusOne = gmp_init(-1);
$computed_value = gmp_sub($minusOne, $value);
switch (true) {
case gmp_intval($computed_value) < 24:
$ai = gmp_intval($computed_value);
$data = null;
break;
case gmp_cmp($computed_value, gmp_init('FF', 16)) < 0:
$ai = 24;
$data = self::hex2bin(str_pad(gmp_strval($computed_value, 16), 2, '0', STR_PAD_LEFT));
break;
case gmp_cmp($computed_value, gmp_init('FFFF', 16)) < 0:
$ai = 25;
$data = self::hex2bin(str_pad(gmp_strval($computed_value, 16), 4, '0', STR_PAD_LEFT));
break;
case gmp_cmp($computed_value, gmp_init('FFFFFFFF', 16)) < 0:
$ai = 26;
$data = self::hex2bin(str_pad(gmp_strval($computed_value, 16), 8, '0', STR_PAD_LEFT));
break;
default:
throw new InvalidArgumentException('Out of range. Please use NegativeBigIntegerTag tag with ByteStringObject object instead.');
}
return new self($ai, $data);
}
public function getValue(): string
{
return $this->getNormalizedData();
}
public function getNormalizedData(bool $ignoreTags = false): string
{
if (null === $this->data) {
return (string) (-1 - $this->additionalInformation);
}
$result = Utils::binToBigInteger($this->data);
$minusOne = BigInteger::of(-1);
return $minusOne->minus($result)->toBase(10);
}
private static function createBigInteger(BigInteger $integer): self
{
if ($integer->isGreaterThanOrEqualTo(BigInteger::zero())) {
throw new InvalidArgumentException('The value must be a negative integer.');
}
$minusOne = BigInteger::of(-1);
$computed_value = $minusOne->minus($integer);
switch (true) {
case $computed_value->isLessThan(BigInteger::of(24)):
$ai = $computed_value->toInt();
$data = null;
break;
case $computed_value->isLessThan(BigInteger::fromBase('FF', 16)):
$ai = 24;
$data = self::hex2bin(str_pad($computed_value->toBase(16), 2, '0', STR_PAD_LEFT));
break;
case $computed_value->isLessThan(BigInteger::fromBase('FFFF', 16)):
$ai = 25;
$data = self::hex2bin(str_pad($computed_value->toBase(16), 4, '0', STR_PAD_LEFT));
break;
case $computed_value->isLessThan(BigInteger::fromBase('FFFFFFFF', 16)):
$ai = 26;
$data = self::hex2bin(str_pad($computed_value->toBase(16), 8, '0', STR_PAD_LEFT));
break;
default:
throw new InvalidArgumentException('Out of range. Please use NegativeBigIntegerTag tag with ByteStringObject object instead.');
}
return new self($ai, $data);
}
private static function hex2bin(string $data): string
{
$result = hex2bin($data);
if (false === $result) {
throw new InvalidArgumentException('Unable to convert the data');
}
return $result;
}
}

View file

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
interface Stream
{
public function read(int $length): string;
}

View file

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use InvalidArgumentException;
use RuntimeException;
final class StringStream implements Stream
{
/**
* @var resource
*/
private $resource;
public function __construct(string $data)
{
$resource = fopen('php://memory', 'rb+');
if (false === $resource) {
throw new RuntimeException('Unable to open the memory');
}
$result = fwrite($resource, $data);
if (false === $result) {
throw new RuntimeException('Unable to write the memory');
}
$result = rewind($resource);
if (false === $result) {
throw new RuntimeException('Unable to rewind the memory');
}
$this->resource = $resource;
}
public function read(int $length): string
{
if (0 === $length) {
return '';
}
$data = fread($this->resource, $length);
if (false === $data) {
throw new RuntimeException('Unable to read the memory');
}
if (mb_strlen($data, '8bit') !== $length) {
throw new InvalidArgumentException(sprintf('Out of range. Expected: %d, read: %d.', $length, mb_strlen($data, '8bit')));
}
return $data;
}
}

View file

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\ByteStringObject;
use CBOR\ByteStringWithChunkObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use CBOR\TextStringObject;
use CBOR\TextStringWithChunkObject;
use InvalidArgumentException;
final class Base16EncodingTag extends Base
{
public static function getTagId(): int
{
return 23;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject && !$object instanceof ByteStringWithChunkObject && !$object instanceof TextStringObject && !$object instanceof TextStringWithChunkObject) {
throw new InvalidArgumentException('This tag only accepts Byte String, Infinite Byte String, Text String or Infinite Text String objects.');
}
return new self(23, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject && !$this->object instanceof ByteStringWithChunkObject && !$this->object instanceof TextStringObject && !$this->object instanceof TextStringWithChunkObject) {
return $this->object->getNormalizedData($ignoreTags);
}
return bin2hex($this->object->getNormalizedData($ignoreTags));
}
}

View file

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\ByteStringObject;
use CBOR\ByteStringWithChunkObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use CBOR\TextStringObject;
use CBOR\TextStringWithChunkObject;
use InvalidArgumentException;
final class Base64EncodingTag extends Base
{
public static function getTagId(): int
{
return 22;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject && !$object instanceof ByteStringWithChunkObject && !$object instanceof TextStringObject && !$object instanceof TextStringWithChunkObject) {
throw new InvalidArgumentException('This tag only accepts Byte String, Infinite Byte String, Text String or Infinite Text String objects.');
}
return new self(22, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject && !$this->object instanceof ByteStringWithChunkObject && !$this->object instanceof TextStringObject && !$this->object instanceof TextStringWithChunkObject) {
return $this->object->getNormalizedData($ignoreTags);
}
$result = base64_decode($this->object->getNormalizedData($ignoreTags), true);
if (false === $result) {
throw new InvalidArgumentException('Unable to decode the data');
}
return $result;
}
}

View file

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use Base64Url\Base64Url;
use CBOR\ByteStringObject;
use CBOR\ByteStringWithChunkObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use CBOR\TextStringObject;
use CBOR\TextStringWithChunkObject;
use InvalidArgumentException;
final class Base64UrlEncodingTag extends Base
{
public static function getTagId(): int
{
return 21;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject && !$object instanceof ByteStringWithChunkObject && !$object instanceof TextStringObject && !$object instanceof TextStringWithChunkObject) {
throw new InvalidArgumentException('This tag only accepts Byte String, Infinite Byte String, Text String or Infinite Text String objects.');
}
return new self(21, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject && !$this->object instanceof ByteStringWithChunkObject && !$this->object instanceof TextStringObject && !$this->object instanceof TextStringWithChunkObject) {
return $this->object->getNormalizedData($ignoreTags);
}
return Base64Url::decode($this->object->getNormalizedData($ignoreTags));
}
}

View file

@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\ListObject;
use CBOR\SignedIntegerObject;
use CBOR\TagObject as Base;
use CBOR\UnsignedIntegerObject;
use function count;
use function extension_loaded;
use InvalidArgumentException;
use RuntimeException;
final class BigFloatTag extends Base
{
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
{
if (!extension_loaded('bcmath')) {
throw new RuntimeException('The extension "bcmath" is required to use this tag');
}
parent::__construct($additionalInformation, $data, $object);
}
public static function getTagId(): int
{
return 5;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ListObject || 2 !== count($object)) {
throw new InvalidArgumentException('This tag only accepts a ListObject object that contains an exponent and a mantissa.');
}
$e = $object->get(0);
if (!$e instanceof UnsignedIntegerObject && !$e instanceof SignedIntegerObject) {
throw new InvalidArgumentException('The exponent must be a Signed Integer or an Unsigned Integer object.');
}
$m = $object->get(1);
if (!$m instanceof UnsignedIntegerObject && !$m instanceof SignedIntegerObject && !$m instanceof NegativeBigIntegerTag && !$m instanceof PositiveBigIntegerTag) {
throw new InvalidArgumentException('The mantissa must be a Positive or Negative Signed Integer or an Unsigned Integer object.');
}
return new self(5, null, $object);
}
public static function createFromExponentAndMantissa(CBORObject $e, CBORObject $m): Base
{
$object = new ListObject();
$object->add($e);
$object->add($m);
return self::create($object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ListObject || 2 !== count($this->object)) {
return $this->object->getNormalizedData($ignoreTags);
}
$e = $this->object->get(0);
$m = $this->object->get(1);
if (!$e instanceof UnsignedIntegerObject && !$e instanceof SignedIntegerObject) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$m instanceof UnsignedIntegerObject && !$m instanceof SignedIntegerObject && !$m instanceof NegativeBigIntegerTag && !$m instanceof PositiveBigIntegerTag) {
return $this->object->getNormalizedData($ignoreTags);
}
return rtrim(
bcmul(
$m->getNormalizedData($ignoreTags),
bcpow(
'2',
$e->getNormalizedData($ignoreTags),
100),
100),
'0'
);
}
}

View file

@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\ListObject;
use CBOR\SignedIntegerObject;
use CBOR\TagObject as Base;
use CBOR\UnsignedIntegerObject;
use function count;
use function extension_loaded;
use InvalidArgumentException;
use RuntimeException;
final class DecimalFractionTag extends Base
{
public function __construct(CBORObject $object)
{
if (!extension_loaded('bcmath')) {
throw new RuntimeException('The extension "bcmath" is required to use this tag');
}
if (!$object instanceof ListObject || 2 !== count($object)) {
throw new InvalidArgumentException('This tag only accepts a ListObject object that contains an exponent and a mantissa.');
}
$e = $object->get(0);
if (!$e instanceof UnsignedIntegerObject && !$e instanceof SignedIntegerObject) {
throw new InvalidArgumentException('The exponent must be a Signed Integer or an Unsigned Integer object.');
}
$m = $object->get(1);
if (!$m instanceof UnsignedIntegerObject && !$m instanceof SignedIntegerObject && !$m instanceof NegativeBigIntegerTag && !$m instanceof PositiveBigIntegerTag) {
throw new InvalidArgumentException('The mantissa must be a Positive or Negative Signed Integer or an Unsigned Integer object.');
}
parent::__construct(4, null, $object);
}
public static function getTagId(): int
{
return 4;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($object);
}
public static function createFromExponentAndMantissa(CBORObject $e, CBORObject $m): Base
{
$object = new ListObject();
$object->add($e);
$object->add($m);
return new self($object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ListObject || 2 !== count($this->object)) {
return $this->object->getNormalizedData($ignoreTags);
}
$e = $this->object->get(0);
$m = $this->object->get(1);
if (!$e instanceof UnsignedIntegerObject && !$e instanceof SignedIntegerObject) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$m instanceof UnsignedIntegerObject && !$m instanceof SignedIntegerObject && !$m instanceof NegativeBigIntegerTag && !$m instanceof PositiveBigIntegerTag) {
return $this->object->getNormalizedData($ignoreTags);
}
return rtrim(
bcmul(
$m->getNormalizedData($ignoreTags),
bcpow(
'10',
$e->getNormalizedData($ignoreTags),
100),
100),
'0'
);
}
}

View file

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use DateTimeImmutable;
final class EpochTag extends Base
{
public static function getTagId(): int
{
return 0;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
return new self(0, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
return DateTimeImmutable::createFromFormat(DATE_RFC3339, $this->object->getNormalizedData($ignoreTags));
}
}

View file

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
final class GenericTag extends Base
{
public static function getTagId(): int
{
return -1;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
return $this->object;
}
}

View file

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use Brick\Math\BigInteger;
use CBOR\ByteStringObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use InvalidArgumentException;
final class NegativeBigIntegerTag extends Base
{
public static function getTagId(): int
{
return 3;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject) {
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
}
return new self(3, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject) {
return $this->object->getNormalizedData($ignoreTags);
}
$integer = BigInteger::fromBase(bin2hex($this->object->getValue()), 16);
$minusOne = BigInteger::of(-1);
return $minusOne->minus($integer)->toBase(10);
}
}

View file

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\ByteStringObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use CBOR\Utils;
use InvalidArgumentException;
final class PositiveBigIntegerTag extends Base
{
public static function getTagId(): int
{
return 2;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject) {
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
}
return new self(2, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject) {
return $this->object->getNormalizedData($ignoreTags);
}
return Utils::hexToString($this->object->getValue());
}
}

View file

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use function array_key_exists;
use Assert\Assertion;
use CBOR\CBORObject;
use CBOR\TagObject;
use CBOR\Utils;
use InvalidArgumentException;
class TagObjectManager
{
/**
* @var string[]
*/
private $classes = [];
public function add(string $class): void
{
if ($class::getTagId() < 0) {
throw new InvalidArgumentException('Invalid tag ID.');
}
$this->classes[$class::getTagId()] = $class;
}
public function getClassForValue(int $value): string
{
return array_key_exists($value, $this->classes) ? $this->classes[$value] : GenericTag::class;
}
public function createObjectForValue(int $additionalInformation, ?string $data, CBORObject $object): TagObject
{
$value = $additionalInformation;
if ($additionalInformation >= 24) {
Assertion::string($data, 'Invalid data');
$value = Utils::binToInt($data);
}
/** @var TagObject $class */
$class = $this->getClassForValue($value);
return $class::createFromLoadedData($additionalInformation, $data, $object);
}
}

View file

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\OtherObject\DoublePrecisionFloatObject;
use CBOR\OtherObject\HalfPrecisionFloatObject;
use CBOR\OtherObject\SinglePrecisionFloatObject;
use CBOR\TagObject as Base;
use CBOR\UnsignedIntegerObject;
use DateTimeImmutable;
use InvalidArgumentException;
use function strval;
final class TimestampTag extends Base
{
public static function getTagId(): int
{
return 1;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof UnsignedIntegerObject && !$object instanceof HalfPrecisionFloatObject && !$object instanceof SinglePrecisionFloatObject && !$object instanceof DoublePrecisionFloatObject) {
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
}
return new self(1, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
switch (true) {
case $this->object instanceof UnsignedIntegerObject:
return DateTimeImmutable::createFromFormat('U', strval($this->object->getNormalizedData($ignoreTags)));
case $this->object instanceof HalfPrecisionFloatObject:
case $this->object instanceof SinglePrecisionFloatObject:
case $this->object instanceof DoublePrecisionFloatObject:
return DateTimeImmutable::createFromFormat('U.u', strval($this->object->getNormalizedData($ignoreTags)));
default:
return $this->object->getNormalizedData($ignoreTags);
}
}
}

View file

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
abstract class TagObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b110;
/**
* @var string|null
*/
protected $data;
/**
* @var CBORObject
*/
protected $object;
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
{
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
$this->object = $object;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->data) {
$result .= $this->data;
}
$result .= (string) $this->object;
return $result;
}
abstract public static function getTagId(): int;
abstract public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): self;
public function getValue(): CBORObject
{
return $this->object;
}
}

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
final class TextStringObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b011;
/**
* @var int|null
*/
private $length;
/**
* @var string
*/
private $data;
public function __construct(string $data)
{
list($additionalInformation, $length) = LengthCalculator::getLengthOfString($data);
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
$this->length = $length;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->length) {
$result .= $this->length;
}
$result .= $this->data;
return $result;
}
public function getValue(): string
{
return $this->data;
}
public function getLength(): int
{
return mb_strlen($this->data, 'utf8');
}
public function getNormalizedData(bool $ignoreTags = false): string
{
return $this->data;
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use InvalidArgumentException;
final class TextStringWithChunkObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b011;
private const ADDITIONAL_INFORMATION = 0b00011111;
/**
* @var TextStringObject[]
*/
private $data = [];
public function __construct()
{
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
}
public function __toString(): string
{
$result = parent::__toString();
foreach ($this->data as $object) {
$result .= (string) $object;
}
$bin = hex2bin('FF');
if (false === $bin) {
throw new InvalidArgumentException('Unable to convert the data');
}
$result .= $bin;
return $result;
}
public function add(TextStringObject $chunk): void
{
$this->data[] = $chunk;
}
public function append(string $chunk): void
{
$this->add(new TextStringObject($chunk));
}
public function getValue(): string
{
$result = '';
foreach ($this->data as $object) {
$result .= $object->getValue();
}
return $result;
}
public function getLength(): int
{
$length = 0;
foreach ($this->data as $object) {
$length += $object->getLength();
}
return $length;
}
public function getNormalizedData(bool $ignoreTags = false): string
{
$result = '';
foreach ($this->data as $object) {
$result .= $object->getNormalizedData($ignoreTags);
}
return $result;
}
}

View file

@ -0,0 +1,167 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use Brick\Math\BigInteger;
use GMP;
use InvalidArgumentException;
final class UnsignedIntegerObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b000;
/**
* @var string|null
*/
private $data;
public function __construct(int $additionalInformation, ?string $data)
{
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->data) {
$result .= $this->data;
}
return $result;
}
public static function createObjectForValue(int $additionalInformation, ?string $data): self
{
return new self($additionalInformation, $data);
}
public static function create(int $value): self
{
return self::createFromString((string) $value);
}
public static function createFromHex(string $value): self
{
$integer = BigInteger::fromBase($value, 16);
return self::createBigInteger($integer);
}
public static function createFromString(string $value): self
{
$integer = BigInteger::of($value);
return self::createBigInteger($integer);
}
/**
* @deprecated Deprecated since v1.1 and will be removed in v2.0. Please use "create" or "createFromString" instead
*/
public static function createFromGmpValue(GMP $value): self
{
if (gmp_cmp($value, gmp_init(0)) < 0) {
throw new InvalidArgumentException('The value must be a positive integer.');
}
switch (true) {
case gmp_cmp($value, gmp_init(24)) < 0:
$ai = gmp_intval($value);
$data = null;
break;
case gmp_cmp($value, gmp_init('FF', 16)) < 0:
$ai = 24;
$data = self::hex2bin(str_pad(gmp_strval($value, 16), 2, '0', STR_PAD_LEFT));
break;
case gmp_cmp($value, gmp_init('FFFF', 16)) < 0:
$ai = 25;
$data = self::hex2bin(str_pad(gmp_strval($value, 16), 4, '0', STR_PAD_LEFT));
break;
case gmp_cmp($value, gmp_init('FFFFFFFF', 16)) < 0:
$ai = 26;
$data = self::hex2bin(str_pad(gmp_strval($value, 16), 8, '0', STR_PAD_LEFT));
break;
default:
throw new InvalidArgumentException('Out of range. Please use PositiveBigIntegerTag tag with ByteStringObject object instead.');
}
return new self($ai, $data);
}
public function getMajorType(): int
{
return self::MAJOR_TYPE;
}
public function getAdditionalInformation(): int
{
return $this->additionalInformation;
}
public function getValue(): string
{
return $this->getNormalizedData();
}
public function getNormalizedData(bool $ignoreTags = false): string
{
if (null === $this->data) {
return (string) $this->additionalInformation;
}
$integer = BigInteger::fromBase(bin2hex($this->data), 16);
return $integer->toBase(10);
}
private static function createBigInteger(BigInteger $integer): self
{
if ($integer->isLessThan(BigInteger::zero())) {
throw new InvalidArgumentException('The value must be a positive integer.');
}
switch (true) {
case $integer->isLessThan(BigInteger::of(24)):
$ai = $integer->toInt();
$data = null;
break;
case $integer->isLessThan(BigInteger::fromBase('FF', 16)):
$ai = 24;
$data = self::hex2bin(str_pad($integer->toBase(16), 2, '0', STR_PAD_LEFT));
break;
case $integer->isLessThan(BigInteger::fromBase('FFFF', 16)):
$ai = 25;
$data = self::hex2bin(str_pad($integer->toBase(16), 4, '0', STR_PAD_LEFT));
break;
case $integer->isLessThan(BigInteger::fromBase('FFFFFFFF', 16)):
$ai = 26;
$data = self::hex2bin(str_pad($integer->toBase(16), 8, '0', STR_PAD_LEFT));
break;
default:
throw new InvalidArgumentException('Out of range. Please use PositiveBigIntegerTag tag with ByteStringObject object instead.');
}
return new self($ai, $data);
}
private static function hex2bin(string $data): string
{
$result = hex2bin($data);
if (false === $result) {
throw new InvalidArgumentException('Unable to convert the data');
}
return $result;
}
}

View file

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use Brick\Math\BigInteger;
/**
* @internal
*/
abstract class Utils
{
public static function binToInt(string $value): int
{
return self::binToBigInteger($value)->toInt();
}
public static function binToBigInteger(string $value): BigInteger
{
return self::hexToBigInteger(bin2hex($value));
}
public static function hexToInt(string $value): int
{
return self::hexToBigInteger($value)->toInt();
}
public static function hexToBigInteger(string $value): BigInteger
{
return BigInteger::fromBase($value, 16);
}
public static function hexToString(string $value): string
{
return BigInteger::fromBase(bin2hex($value), 16)->toBase(10);
}
public static function intToHex(int $value): string
{
return BigInteger::of($value)->toBase(16);
}
}