fab2s/dt0 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

fab2s/dt0

Composer 安装命令:

composer require fab2s/dt0

包简介

Immutable DTOs with bidirectional casting. No framework required. 8x faster than the alternative.

README 文档

README

CI QA codecov PHPStan Latest Stable Version Total Downloads PRs Welcome License

Immutable PHP DTOs with bidirectional casting. No framework required. ~8x faster than the alternative.

Dt0 (DeeTO) is a PHP 8.2+ Data Transfer Object implementation that uses native readonly properties for true immutability. One #[Cast] attribute handles input transformation, output formatting, defaults, and property renaming. Compiled once per class, fast always.

Quick Start

use fab2s\Dt0\Dt0;
use fab2s\Dt0\Attribute\Cast;
use fab2s\Dt0\Caster\DateTimeCaster;
use fab2s\Dt0\Caster\DateTimeFormatCaster;

class UserDto extends Dt0
{
    public readonly int $id;
    public readonly string $name;
    public readonly string $email;

    #[Cast(
        in: DateTimeCaster::class,
        out: new DateTimeFormatCaster('Y-m-d'),
    )]
    public readonly DateTimeImmutable $createdAt;

    #[Cast(default: 'user')]
    public readonly string $role;
}

// Create from anything
$user = UserDto::make(id: 1, name: 'Jane', email: 'jane@example.com', createdAt: '2024-01-15');
$user = UserDto::fromArray($apiResponse);
$user = UserDto::fromJson($jsonString);

// Access properties
$user->name;       // 'Jane'
$user->createdAt;  // DateTimeImmutable instance
$user->role;       // 'user' (default applied)

// Output with casting applied
$user->toArray();  // [..., 'createdAt' => DateTimeImmutable, ...]
$user->toJson();   // {..., "createdAt": "2024-01-15", ...}

// Immutable updates
$admin = $user->update(role: 'admin');
$user->role;   // 'user' (unchanged)
$admin->role;  // 'admin' (new instance)

Installation

composer require fab2s/dt0

For Laravel model casting integration, see laravel-dt0.

Why Dt0

  • Real immutability, enforced by PHP. Native readonly properties — accidental writes cause fatal errors, not silent bugs.
  • One attribute to rule them all. #[Cast] handles input transformation, output formatting, defaults, and property renaming in a single, composable attribute.
  • Framework-agnostic. Use anywhere PHP runs — including standalone validation powered by Laravel's validation engine, without the framework.
  • Compiled once, fast always. Reflection and metadata processed once per class, then cached. ~8x faster than spatie/laravel-data for typical operations.

Creating Instances

Method Input On Failure
make(...$args) Named/positional args Throws
fromArray(array) Associative array Throws
fromJson(string) JSON string Throws
fromGz(string) Gzipped JSON Throws
from(mixed) Array, JSON, or Dt0 Throws
tryFrom(mixed) Array, JSON, or Dt0 Returns null

Custom constructors with promoted properties are supported. See Creating Instances for constructors, new vs factory methods, and edge cases.

Casting

Dt0 supports bidirectional casting: transform values on the way in (hydration) and out (serialization).

use fab2s\Dt0\Attribute\Cast;
use fab2s\Dt0\Caster\DateTimeCaster;
use fab2s\Dt0\Caster\DateTimeFormatCaster;
use fab2s\Dt0\Caster\ScalarCaster;
use fab2s\Dt0\Caster\ScalarType;

class ArticleDto extends Dt0
{
    public readonly string $title;

    #[Cast(in: new ScalarCaster(ScalarType::int))]
    public readonly int $viewCount;

    #[Cast(
        in: DateTimeCaster::class,
        out: new DateTimeFormatCaster(DateTimeFormatCaster::ISO),
    )]
    public readonly DateTimeImmutable $publishedAt;
}

$article = ArticleDto::make(
    title: 'Hello World',
    viewCount: '42',           // string -> int
    publishedAt: '2024-01-15', // string -> DateTimeImmutable
);

Class-level casting with #[Casts]:

#[Casts(
    status: new Cast(default: 'pending'),
    priority: new Cast(default: 0),
    createdAt: new Cast(in: DateTimeCaster::class),
)]
class TaskDto extends Dt0
{
    public readonly string $title;
    public readonly string $status;
    public readonly int $priority;
    public readonly DateTime $createdAt;
}

Built-in types — Enums and nested Dt0 classes are handled automatically, no caster needed:

class OrderDto extends Dt0
{
    public readonly Status $status;      // BackedEnum — auto-cast from string/int
    public readonly AddressDto $address; // Nested Dt0 — auto-cast from array/JSON
}

Available Casters

Caster Description
ScalarCaster Cast to int, float, bool, string
JsonCaster Decode JSON on input, encode on output
TrimCaster Trim strings (ltrim, rtrim, custom characters)
Base64Caster Decode base64 on input, encode on output
DateTimeCaster Parse to DateTime or DateTimeImmutable
DateTimeFormatCaster Format DateTime for output
CarbonCaster Parse to Carbon (requires nesbot/carbon)
Dt0Caster Cast to nested Dt0 instances
ArrayOfCaster Cast typed arrays (Dt0, Enum, or scalar)
ClassCaster Instantiate arbitrary classes
MathCaster Precision numbers (requires fab2s/math)
CasterCollection Chain multiple casters in a pipeline

See Casters Documentation for detailed usage, bidirectional casting, and custom casters.

Validation

Dt0 provides standalone validation powered by Laravel's validation engine — no Laravel framework required. In Laravel applications, it auto-detects the framework and uses Laravel's validator transparently.

Requires illuminate/validation and illuminate/translation (v11+):

composer require "illuminate/validation:^11.0|^12.0" "illuminate/translation:^11.0|^12.0"
use fab2s\Dt0\Dt0;
use fab2s\Dt0\Attribute\Rule;
use fab2s\Dt0\Attribute\Rules;
use fab2s\Dt0\Attribute\Validate;
use fab2s\Dt0\Validator\Validator;

#[Validate(Validator::class)]
#[Rules(
    email: new Rule('required|email'),
)]
class ContactDto extends Dt0
{
    public readonly string $email;

    #[Rule('required|string|min:2|max:100')]
    public readonly string $name;

    #[Rule('nullable|string|max:1000')]
    public readonly ?string $message;
}

// Throws ValidationException on failure
$contact = ContactDto::withValidation(
    email: 'test@example.com',
    name: 'John',
    message: 'Hello!',
);

Rules can be defined at three levels with clear priority: property #[Rule] > class #[Rules] > #[Validate] rules. The ValidatorInterface is open for custom implementations.

See Validation Documentation for locale configuration, custom translations, and custom validators.

Output

$dto->toArray();      // Array with objects intact, output casters applied
$dto->toJsonArray();  // Array with objects serialized (JsonSerializable)
$dto->toJson();       // JSON string
$dto->toGz();         // Gzipped JSON string
json_encode($dto);    // JSON (implements JsonSerializable)
(string) $dto;        // JSON (implements Stringable)

Output Filtering

// Exclude sensitive fields
$dto->without('password', 'apiKey')->toJson();

// Include only specific fields
$dto->only('id', 'name')->toArray();

// Add computed or protected properties
$dto->with('fullName', fn($d) => "$d->firstName $d->lastName")->toArray();
$dto->with('total', true)->toArray();       // calls getTotal()
$dto->with('internalField')->toArray();     // exposes protected property

Declarative output control with #[With] is also supported. See Output Documentation for details.

Immutable Operations

$copy = $dto->clone();
$updated = $dto->update(name: 'Jane', role: 'admin');
$dto->equals($updated);  // false

// Serialization round-trip
$restored = unserialize(serialize($dto));
$dto->equals($restored);  // true

Property Renaming

Map between external names (APIs, databases) and internal property names:

class ApiResponseDto extends Dt0
{
    #[Cast(renameFrom: 'created_at', renameTo: 'createdAtStr')]
    public readonly string $createdAt;

    // Accept multiple input names
    #[Cast(renameFrom: ['user_name', 'username', 'login'])]
    public readonly string $userName;
}

All renameTo values are automatically added to renameFrom, ensuring round-trip consistency.

Default Values

class ConfigDto extends Dt0
{
    #[Cast(default: 3600)]
    public readonly int $ttl;

    #[Cast(default: null)]
    public readonly ?string $prefix;

    #[Cast(default: true)]
    public readonly bool $enabled;
}

$config = ConfigDto::make();  // All defaults applied

Resolution order: provided value > Cast default > nullable default > promoted parameter default.

Attribute Inheritance

Dt0 resolves attributes up the parent class chain — both property-level and class-level:

class TimestampedDto extends Dt0
{
    #[Cast(in: DateTimeCaster::class, out: new DateTimeFormatCaster(DateTimeFormatCaster::ISO))]
    public readonly DateTimeImmutable $createdAt;
}

// Inherits Cast from TimestampedDto
class ArticleDto extends TimestampedDto
{
    public readonly string $title;
    public readonly DateTimeImmutable $createdAt;  // Redeclare for PHP < 8.4
}

$article = ArticleDto::make(title: 'Hello', createdAt: '2024-01-15');
$article->createdAt;  // DateTimeImmutable (inherited cast applied)

See Inheritance Documentation for multi-level inheritance, class attribute inheritance, and override patterns.

Performance

Reflection and attribute metadata compiled once per class, per process. Subsequent instantiations reuse cached data with zero reflection overhead.

Benchmarks

Dt0 vs spatie/laravel-data (PHP 8.5, 10,000 iterations)

Operation Dt0 spatie/laravel-data Speedup
Simple DTO (8 props, 5 casts) 2.6 µs 17.1 µs ~6.5x faster
Complex DTO (nested + arrays) 11.6 µs 67.7 µs ~5.8x faster
Round-trip (json->dto->json) 5.0 µs 31.8 µs ~6.4x faster

Repeated serialization (same instance):

Operation Dt0 spatie/laravel-data Speedup
toArray() (simple) 0.085 µs 9.1 µs ~107x faster
toArray() (nested) 0.103 µs 30.1 µs ~292x faster
toJson() 0.034 µs 9.3 µs ~277x faster

Output caching delivers ~107-292x improvements when serializing the same instance multiple times (API + logging, event sourcing, queue + monitoring, caching layers). Absolute timings are hardware-dependent; the speedup ratios are what matter.

php benchmark/compare-spatie.php

Extending

Dt0's attributes, casters, and validators are extensible. See Extending Documentation for interfaces, abstract classes, and compiled metadata access.

Exceptions

All exceptions extend ContextException with structured context for debugging:

Exception Usage
Dt0Exception General DTO errors (missing properties, invalid input)
CasterException Casting failures
AttributeException Attribute configuration errors

Requirements

  • PHP 8.2, 8.3, 8.4, or 8.5

Dependencies

Optional

Contributing

Contributions are welcome. Please open issues and submit pull requests.

composer fix        # Code style (Laravel Pint)
composer test       # Run tests
composer cov        # Tests with coverage
composer stan       # PHPStan level 9 (src/)
composer stan-tests # PHPStan level 5 (tests/)

License

Dt0 is open-sourced software licensed under the MIT license.

fab2s/dt0 适用场景与选型建议

fab2s/dt0 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2.47k 次下载、GitHub Stars 达 10, 最近一次更新时间为 2024 年 04 月 21 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「symfony」 「json」 「serialization」 「validation」 「laravel」 「dto」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 fab2s/dt0 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 fab2s/dt0 我们能提供哪些服务?
定制开发 / 二次开发

基于 fab2s/dt0 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 2.47k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 10
  • 点击次数: 25
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 10
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2024-04-21