From f50d6be73588d61b5d38cd8b01d61033d94f2ec0 Mon Sep 17 00:00:00 2001 From: ck Date: Wed, 25 Mar 2026 14:37:10 +0800 Subject: [PATCH] feat: configure and generate API Documentation (Issue #45) - Add phpdoc.xml configuration - Add docs/API.md (API overview, usage examples) - Add docs/TYPES.md (detailed types reference) - Update Makefile with make docs target Closes #45 --- Makefile | 9 +- docs/API.md | 257 ++++++++++++++++++++++++++++++++++++++++ docs/TYPES.md | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++ phpdoc.xml | 50 ++++++++ 4 files changed, 629 insertions(+), 3 deletions(-) create mode 100644 docs/API.md create mode 100644 docs/TYPES.md create mode 100644 phpdoc.xml diff --git a/Makefile b/Makefile index f1d142d..c3002fc 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: sniff test coverage stan cs bench bench-full bench-quick all compat +.PHONY: sniff test coverage stan cs bench bench-full bench-quick all compat docs sniff: vendor/autoload.php ## Detects code style issues with phpcs vendor/bin/phpcs --standard=PSR12 src tests -n @@ -24,11 +24,14 @@ bench-full: vendor/autoload.php ## Run full benchmarks for CI bench-quick: vendor/autoload.php ## Run quick benchmarks vendor/bin/phpbench run --profile=quick --report=default -all: test stan cs ## Run all checks (tests, static analysis, code style) - compat: vendor/autoload.php ## Run polkadot.js compatibility tests php compat/tests/php-compatibility-test.php +docs: ## Generate API documentation + vendor/bin/phpdoc + +all: test stan cs ## Run all checks (tests, static analysis, code style) + vendor/autoload.php: composer install --no-interaction --prefer-dist diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..95e1bc3 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,257 @@ +# php-scale-codec API Documentation + +Version 2.0.0 + +## Overview + +php-scale-codec is a PHP implementation of the SCALE (Simple Concatenated Aggregate Little-Endian) codec used in Substrate-based blockchains like Polkadot. + +## Installation + +```bash +composer require gmajor/substrate-codec-php +``` + +## Quick Start + +```php +get('U32'); +$encoded = $u32->encode(12345); +echo $encoded->toHex(); // 0x39300000 + +// Decode it back +$bytes = ScaleBytes::fromHex('0x39300000'); +$decoded = $u32->decode($bytes); +echo $decoded; // 12345 +``` + +## Core Components + +### TypeRegistry + +Central registry for all SCALE types. + +```php +$registry = new TypeRegistry(); + +// Get primitive types +$u8 = $registry->get('U8'); +$u32 = $registry->get('U32'); +$bool = $registry->get('Bool'); + +// Get complex types +$vec = $registry->get('Vec'); +$option = $registry->get('Option'); +``` + +### ScaleBytes + +Container for SCALE-encoded bytes. + +```php +// Create from hex +$bytes = ScaleBytes::fromHex('0x010203'); + +// Create from byte array +$bytes = ScaleBytes::fromBytes([1, 2, 3]); + +// Convert to hex +$hex = $bytes->toHex(); + +// Read bytes +$byte = $bytes->readByte(); +$bytes = $bytes->readBytes(4); +``` + +### TypeFactory + +Factory for creating parameterized types. + +```php +use Substrate\ScaleCodec\Types\TypeFactory; + +$factory = new TypeFactory($registry); + +// Create Vec +$vecU8 = $factory->create('Vec'); + +// Create Option +$optionU32 = $factory->create('Option'); + +// Create tuple +$tuple = $factory->create('(U8, U32, U64)'); +``` + +## Primitive Types + +| Type | Description | Range | +|------|-------------|-------| +| U8 | Unsigned 8-bit | 0 - 255 | +| U16 | Unsigned 16-bit | 0 - 65,535 | +| U32 | Unsigned 32-bit | 0 - 4,294,967,295 | +| U64 | Unsigned 64-bit | 0 - 18,446,744,073,709,551,615 | +| U128 | Unsigned 128-bit | 0 - 2^128 - 1 | +| I8 | Signed 8-bit | -128 - 127 | +| I16 | Signed 16-bit | -32,768 - 32,767 | +| I32 | Signed 32-bit | -2^31 - 2^31 - 1 | +| I64 | Signed 64-bit | -2^63 - 2^63 - 1 | +| I128 | Signed 128-bit | -2^127 - 2^127 - 1 | +| Bool | Boolean | true/false | +| String | UTF-8 string | any length | + +## Compound Types + +### Vec + +Dynamically sized vector of elements. + +```php +$vec = new VecType($registry); +$vec->setElementType($registry->get('U8')); + +// Encode +$encoded = $vec->encode([1, 2, 3, 4, 5]); + +// Decode +$decoded = $vec->decode(ScaleBytes::fromBytes($encoded->toBytes())); +``` + +### Option + +Optional value (Some or None). + +```php +$option = new OptionType($registry); +$option->setInnerType($registry->get('U32')); + +// Encode Some +$encoded = $option->encode(42); + +// Encode None +$encoded = $option->encode(null); +``` + +### Struct + +Fixed structure with named fields. + +```php +$struct = new StructType($registry); +$struct->setFields([ + 'id' => $registry->get('U32'), + 'name' => $registry->get('String'), + 'active' => $registry->get('Bool'), +]); + +$encoded = $struct->encode([ + 'id' => 1, + 'name' => 'Alice', + 'active' => true, +]); +``` + +### Enum + +Enumerated type with variants. + +```php +$enum = new EnumType($registry); +$enum->addVariant('None', 0); +$enum->addVariant('Some', 1, $registry->get('U32')); + +$encoded = $enum->encode(['Some' => 42]); +``` + +## Compact Encoding + +SCALE uses a compact encoding for variable-length integers. + +```php +$compact = $registry->get('Compact'); + +// Small values (0-63): 1 byte +$compact->encode(42); // 0xa8 + +// Medium values (64-16383): 2 bytes +$compact->encode(100); // 0x9101 + +// Large values: 4+ bytes +$compact->encode(1000000); +``` + +## Metadata + +Parse Substrate metadata for dynamic type resolution. + +```php +use Substrate\ScaleCodec\Metadata\MetadataParser; + +$parser = new MetadataParser(); +$metadata = $parser->parse($metadataHex); + +// Access pallets +$pallet = $metadata->getPallet('System'); + +// Get events +$events = $metadata->getPalletEvents('System'); +``` + +## Extrinsic + +Create and sign extrinsics. + +```php +use Substrate\ScaleCodec\Extrinsic\ExtrinsicBuilder; + +$builder = new ExtrinsicBuilder($registry); +$extrinsic = $builder + ->setVersion(4) + ->setPallet('Balances') + ->setFunction('transfer') + ->setArgs([ + 'dest' => $accountId, + 'value' => 1000000000, + ]) + ->sign($keypair) + ->build(); +``` + +## Error Handling + +```php +use Substrate\ScaleCodec\Exception\ScaleEncodeException; +use Substrate\ScaleCodec\Exception\ScaleDecodeException; + +try { + $encoded = $u32->encode(4294967296); // Overflow +} catch (ScaleEncodeException $e) { + echo "Encoding error: " . $e->getMessage(); +} + +try { + $decoded = $u32->decode($insufficientBytes); +} catch (ScaleDecodeException $e) { + echo "Decoding error: " . $e->getMessage(); +} +``` + +## Versioning + +- Supports Substrate Metadata v12-v15 +- SCALE codec specification compliant +- polkadot.js compatible + +## See Also + +- [SCALE Codec Specification](https://docs.substrate.io/reference/scale-codec/) +- [Substrate Documentation](https://docs.substrate.io/) +- [polkadot.js API](https://polkadot.js.org/docs/api/) diff --git a/docs/TYPES.md b/docs/TYPES.md new file mode 100644 index 0000000..7c3282c --- /dev/null +++ b/docs/TYPES.md @@ -0,0 +1,316 @@ +# Types Reference + +This document provides detailed reference for all SCALE types. + +## Table of Contents + +1. [Primitive Types](#primitive-types) +2. [Compact Types](#compact-types) +3. [Compound Types](#compound-types) +4. [Special Types](#special-types) +5. [Metadata Types](#metadata-types) + +--- + +## Primitive Types + +### Unsigned Integers + +#### U8 + +8-bit unsigned integer (0-255). + +```php +$type = $registry->get('U8'); + +// Encoding +$encoded = $type->encode(255); // 0xff +$encoded = $type->encode(0); // 0x00 + +// Decoding +$value = $type->decode(ScaleBytes::fromHex('0xff')); // 255 +``` + +#### U16 + +16-bit unsigned integer (0-65535). Little-endian. + +```php +$type = $registry->get('U16'); +$encoded = $type->encode(256); // 0x0001 +``` + +#### U32 + +32-bit unsigned integer. Little-endian. + +```php +$type = $registry->get('U32'); +$encoded = $type->encode(1000000); // 0x40420f00 +``` + +#### U64 + +64-bit unsigned integer. Returns string for values > PHP_INT_MAX. + +```php +$type = $registry->get('U64'); +$encoded = $type->encode('18446744073709551615'); // Max value +$decoded = $type->decode($bytes); // Returns string +``` + +#### U128 + +128-bit unsigned integer. Always returns string. + +```php +$type = $registry->get('U128'); +$encoded = $type->encode('340282366920938463463374607431768211455'); +``` + +### Signed Integers + +#### I8 + +8-bit signed integer (-128 to 127). + +```php +$type = $registry->get('I8'); +$encoded = $type->encode(-1); // 0xff +$encoded = $type->encode(-128); // 0x80 +``` + +#### I16, I32, I64, I128 + +Signed versions follow two's complement encoding. + +--- + +## Compact Types + +### Compact + +Variable-length integer encoding for efficient storage. + +| Range | Prefix | Bytes | +|-------|--------|-------| +| 0-63 | 0b00 | 1 | +| 64-16383 | 0b01 | 2 | +| 16384-1073741823 | 0b10 | 4 | +| >1073741823 | 0b11 | variable | + +```php +$compact = $registry->get('Compact'); + +// Single byte mode (0-63) +$compact->encode(0); // 0x00 +$compact->encode(63); // 0xfc + +// Two byte mode (64-16383) +$compact->encode(64); // 0x0101 +$compact->encode(16383); // 0xfdff + +// Four byte mode +$compact->encode(16384); // 0x02000000 +``` + +--- + +## Compound Types + +### Vec\ + +Dynamically sized sequence of elements. + +```php +$vecU8 = (new VecType($registry))->setElementType($registry->get('U8')); + +// Empty vector +$vecU8->encode([]); // 0x00 + +// Non-empty vector +$vecU8->encode([1, 2, 3]); // 0x0c010203 +``` + +### Option\ + +Optional value: None (0x00) or Some (0x01 + value). + +```php +$optionU8 = (new OptionType($registry))->setInnerType($registry->get('U8')); + +$optionU8->encode(null); // 0x00 +$optionU8->encode(42); // 0x012a +``` + +### Tuple + +Fixed-size sequence of heterogeneous types. + +```php +$tuple = (new TupleType($registry)) + ->addElementType($registry->get('U8')) + ->addElementType($registry->get('U32')); + +$tuple->encode([255, 1000000]); // 0xff40420f00 +``` + +### Struct + +Named fields with specific types. + +```php +$struct = (new StructType($registry))->setFields([ + 'id' => $registry->get('U32'), + 'name' => $registry->get('String'), +]); + +$struct->encode([ + 'id' => 1, + 'name' => 'test', +]); +``` + +### Enum + +Tagged union with variants. + +```php +$enum = new EnumType($registry); +$enum->addVariant('VariantA', 0); +$enum->addVariant('VariantB', 1, $registry->get('U32')); + +// Unit variant +$enum->encode(['VariantA' => null]); // 0x00 + +// Variant with data +$enum->encode(['VariantB' => 42]); // 0x012a000000 +``` + +### Result\ + +Result type with success or error. + +```php +$result = (new ResultType($registry)) + ->setOkType($registry->get('U32')) + ->setErrType($registry->get('String')); + +// Ok +$result->encode(['Ok' => 42]); // 0x002a000000 + +// Err +$result->encode(['Err' => 'error']); // 0x01106572726f72 +``` + +--- + +## Special Types + +### Bool + +Boolean value. + +```php +$bool = $registry->get('Bool'); +$bool->encode(false); // 0x00 +$bool->encode(true); // 0x01 +``` + +### String / Text + +UTF-8 encoded string with length prefix. + +```php +$string = $registry->get('String'); +$string->encode('Hello'); // 0x1448656c6c6f +``` + +### Bytes + +Raw byte sequence. + +```php +$bytes = $registry->get('Bytes'); +$bytes->encode([1, 2, 3, 4]); +``` + +### AccountId + +32-byte account identifier. + +```php +$accountId = $registry->get('AccountId'); +$accountId->encode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'); +``` + +### MultiAddress + +Multi-address type for various address formats. + +```php +$address = $registry->get('MultiAddress'); +``` + +--- + +## Metadata Types + +### Metadata + +Parsed Substrate metadata. + +```php +$parser = new MetadataParser(); +$metadata = $parser->parse($metadataHex); +``` + +### Pallet + +Individual pallet/module information. + +```php +$pallet = $metadata->getPallet('System'); +``` + +### TypeDefinition + +Runtime type definition from metadata. + +--- + +## Type Validation + +All types support validation: + +```php +$type->isValid($value); // Returns true/false + +// Example +$u8->isValid(255); // true +$u8->isValid(256); // false +$u8->isValid(-1); // false +``` + +--- + +## Error Handling + +```php +use Substrate\ScaleCodec\Exception\ScaleEncodeException; +use Substrate\ScaleCodec\Exception\ScaleDecodeException; + +// Encoding errors +try { + $u8->encode(256); // Out of range +} catch (ScaleEncodeException $e) { + // Handle error +} + +// Decoding errors +try { + $u32->decode($insufficientBytes); +} catch (ScaleDecodeException $e) { + // Handle error +} +``` diff --git a/phpdoc.xml b/phpdoc.xml new file mode 100644 index 0000000..c1daa33 --- /dev/null +++ b/phpdoc.xml @@ -0,0 +1,50 @@ + + + + docs/api + cache/phpdocumentor + + + + + + src + + + + docs/api + + + + + + php + + + Substrate\ScaleCodec + + + TODO + FIXME + + + + + + docs/guides + + + + + php-scale-codec API Documentation + +