maxi-format/maxi
最新稳定版本:0.1.0
Composer 安装命令:
composer require maxi-format/maxi
包简介
PHP library for parsing and dumping MAXI schema
README 文档
README
PHP 8.0+ library for parsing and dumping MAXI schema + records.
Version: 0.1.0
Install
composer require maxi-format/maxi
API overview
| Method | Description |
|---|---|
Maxi::parse($input, $options) |
Parse MAXI text → MaxiParseResult (schema + raw records) |
Maxi::stream($input, $options) |
Parse schema eagerly, yield records lazily via PHP generator |
Maxi::parseAs($input, $classMap, $options) |
Parse + hydrate records into class instances |
Maxi::parseAutoAs($input, $classes, $options) |
Same, with alias inferred from #[MaxiType] attributes |
Maxi::dump($data, $options) |
Serialize objects / parse results → MAXI text |
Maxi::dumpAuto($objects, $options) |
Same, with schema inferred from #[MaxiType]/#[MaxiField] attributes |
All methods are also available as standalone functions in the Maxi\Api namespace
(e.g. Maxi\Api\parseMaxi()).
Documentation
- docs/parser.md — full parser guide:
Maxi::parse,Maxi::stream,Maxi::parseAs,Maxi::parseAutoAs, hydration, reference resolution, options - docs/dumper.md — full dumper guide:
Maxi::dump,Maxi::dumpAuto, schema-annotated classes, references, inheritance, options
Quick start
Parse
use Maxi\Maxi; $input = <<<MAXI U:User(id:int|name|email) ### U(1|Julie|julie@maxi.org) U(2|Matt|matt@maxi.org) MAXI; $result = Maxi::parse($input); echo $result->records[0]->values[1]; // 'Julie' echo $result->schema->getType('U')->name; // 'User'
Parse into class instances
use Maxi\Maxi; use Maxi\Attribute\MaxiType; use Maxi\Attribute\MaxiField; #[MaxiType(alias: 'U', name: 'User')] class User { #[MaxiField(typeExpr: 'int', id: true)] public int $id; #[MaxiField(required: true)] public string $name; #[MaxiField(annotation: 'email')] public ?string $email = null; } $result = Maxi::parseAutoAs($input, [User::class]); $user = $result->data['U'][0]; echo $user->name; // 'Julie' echo $user->email; // 'julie@maxi.org'
Or with an explicit alias → class map:
$result = Maxi::parseAs($input, ['U' => User::class]);
Dump
Round-trip from a parse result:
$maxi = Maxi::dump($result);
From PHP objects with auto-detected schema:
$maxi = Maxi::dumpAuto([ new User(id: 1, name: 'Julie', email: 'julie@maxi.org'), ]);
With explicit type definitions:
use function Maxi\Api\dumpMaxi; $maxi = dumpMaxi([['id' => 1, 'name' => 'Julie']], [ 'defaultAlias' => 'U', 'types' => [[ 'alias' => 'U', 'name' => 'User', 'fields' => [ ['name' => 'id', 'typeExpr' => 'int'], ['name' => 'name'], ], ]], ]);
Stream (lazy record parsing)
For large files, use streaming to avoid loading all records into memory at once. The schema is parsed eagerly; records are yielded one at a time via a PHP generator:
$stream = Maxi::stream($input); echo $stream->schema->getType('U')->name; // 'User' foreach ($stream as $record) { echo $record->alias; // 'U' echo $record->values[1]; // field values }
stream() also accepts a file handle (resource):
$fh = fopen('data.maxi', 'r'); $stream = Maxi::stream($fh);
PHP Attributes — Doctrine-style annotations
Map MAXI types to PHP classes using native PHP 8 Attributes:
use Maxi\Attribute\MaxiType; use Maxi\Attribute\MaxiField; #[MaxiType(alias: 'O', name: 'Order')] class Order { #[MaxiField(typeExpr: 'int', id: true)] public int $id; #[MaxiField(typeExpr: 'U')] public int|User $user; #[MaxiField(typeExpr: 'decimal')] public string $total; }
MaxiField supports the following options:
| Option | Type | Description |
|---|---|---|
typeExpr |
?string |
MAXI type expression ('int', 'str[]', 'enum[a,b]', 'U') |
annotation |
?string |
Type annotation ('email', 'base64', 'hex') |
required |
bool |
Adds the ! (required) constraint |
id |
bool |
Marks this field as the record identifier |
defaultValue |
mixed |
Default value when field is omitted |
name |
?string |
Override the serialized field name (defaults to property name) |
constraints |
?string |
Raw constraint string (e.g. '>=3,<=50') |
Schema registry for third-party classes
For classes you don't own (no Attributes), register a schema descriptor manually:
use Maxi\Registry\MaxiSchemaRegistry; MaxiSchemaRegistry::define(ThirdPartyUser::class, [ 'alias' => 'U', 'name' => 'User', 'fields' => [ ['name' => 'id', 'typeExpr' => 'int', 'constraints' => [['type' => 'id']]], ['name' => 'name'], ['name' => 'email', 'annotation' => 'email'], ], ]);
Error handling
All parse/dump errors throw Maxi\Core\MaxiException which carries structured context:
use Maxi\Core\MaxiException; try { Maxi::parse($input, ['allowTypeCoercion' => 'error']); } catch (MaxiException $e) { echo $e->errorCode; // e.g. 'E402' echo $e->maxiLine; // line number where the error occurred echo $e->maxiFilename; // filename (if provided via options) echo $e->getMessage(); // human-readable description }
Error codes
| Code | Name | Description |
|---|---|---|
| E101 | InvalidSyntaxError | General syntax error |
| E102 | DuplicateTypeError | Duplicate type alias in schema |
| E103 | UnknownDirectiveError | Unrecognised @directive |
| E201 | UnknownTypeError | Record uses undefined type alias |
| E202 | UndefinedParentError | Parent type not defined |
| E203 | CircularInheritanceError | Circular type inheritance |
| E204 | UnresolvedReferenceError | Reference ID not found |
| E205 | DuplicateIdentifierError | Duplicate id in records |
| E301 | ConstraintSyntaxError | Malformed constraint |
| E302 | InvalidConstraintValueError | Invalid constraint value |
| E303 | ConstraintViolationError | Value violates a constraint |
| E304 | ArraySyntaxError | Malformed array literal |
| E401 | SchemaMismatchError | Too many/few values for type |
| E402 | TypeMismatchError | Value doesn't match field type |
| E403 | MissingRequiredFieldError | Required field is null/missing |
| E404 | InvalidDefaultValueError | Default value type mismatch |
| E405 | UnsupportedBinaryFormatError | Invalid bytes annotation |
| E501 | EnumAliasError | Duplicate or ambiguous enum alias |
| E601 | UnsupportedVersionError | @version value not supported |
| E602 | SchemaLoadError | Error loading external schema |
| E603 | StreamError | Error during streaming parse |
Parser behavior options
By default, the parser warns on type mismatches, missing fields, and constraint violations instead of throwing. Tune this per option:
// Warn on type mismatches (default) $result = Maxi::parse($input); foreach ($result->warnings as $w) { echo "[{$w->code}] {$w->message} (line {$w->line})\n"; }
To make any of these fatal, set the relevant option to 'error':
// Throw on type mismatches $result = Maxi::parse($input, ['allowTypeCoercion' => 'error']); // Throw on any schema violation $result = Maxi::parse($input, [ 'allowTypeCoercion' => 'error', 'allowMissingFields' => 'error', 'allowAdditionalFields' => 'error', 'allowConstraintViolations' => 'error', ]);
External schema imports
MAXI files can import type definitions from external .mxs schema files.
Provide a loadSchema callback to resolve them:
$result = Maxi::parse($input, [ 'loadSchema' => function (string $path): string { return file_get_contents(__DIR__ . '/schemas/' . $path); }, ]);
MAXI format (quick reference)
U:User(id:int|name|email=unknown) ← type definition
### ← section separator
U(1|Julie|~) ← record (~ = explicit null)
- Omitted trailing fields use their declared default value.
~sets a field to explicitnull, even if it has a default.- Arrays:
[1,2,3]— Maps:{key:value,key2:value2} - Inline objects:
O(100|(1|Julie|julie@maxi.org)|99.99) - See the MAXI spec for the full format definition.
Test
composer test
License
Released under the MIT License.
统计信息
- 总下载量: 3
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 10
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-05-06