$value) { $method = 'set' . str_replace('_', '', ucwords($key, '_')); if (method_exists($dto, $method)) { $dto->$method($value); } elseif (property_exists($dto, $key)) { $dto->$key = $value; } } return $dto; } /** * DTO转数组 */ public function toArray(): array { $data = []; $reflection = new \ReflectionClass($this); foreach ($reflection->getProperties() as $property) { $propertyName = $property->getName(); $method = 'get' . str_replace('_', '', ucwords($propertyName, '_')); if (method_exists($this, $method)) { $data[$propertyName] = $this->$method(); } else { $data[$propertyName] = $this->$propertyName ?? null; } } return $data; } /** * DTO转JSON */ public function toJson(): string { return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE); } /** * JSON转DTO */ public static function fromJson(string $json): static { $data = json_decode($json, true); if (!is_array($data)) { throw new \InvalidArgumentException('Invalid JSON data'); } return static::fromArray($data); } /** * 验证DTO数据 */ public function validate(): array { $errors = []; $reflection = new \ReflectionClass($this); foreach ($reflection->getProperties() as $property) { $propertyName = $property->getName(); $value = $this->$propertyName ?? null; // 检查必填字段 $attributes = $property->getAttributes('Required'); if (!empty($attributes) && ($value === null || $value === '')) { $errors[$propertyName] = "Field {$propertyName} is required"; continue; } // 检查数据类型 $type = $property->getType(); if ($type && $value !== null) { $typeName = $type->getName(); if (!$this->validateType($value, $typeName)) { $errors[$propertyName] = "Field {$propertyName} must be of type {$typeName}"; } } } return $errors; } /** * 验证数据类型 */ private function validateType(mixed $value, string $type): bool { return match ($type) { 'int', 'integer' => is_int($value), 'float', 'double' => is_float($value), 'string' => is_string($value), 'bool', 'boolean' => is_bool($value), 'array' => is_array($value), 'object' => is_object($value), default => true, }; } /** * 获取所有属性 */ public function getProperties(): array { $reflection = new \ReflectionClass($this); $properties = []; foreach ($reflection->getProperties() as $property) { $properties[$property->getName()] = $this->{$property->getName()} ?? null; } return $properties; } /** * 设置属性 */ public function setProperty(string $name, mixed $value): void { $method = 'set' . str_replace('_', '', ucwords($name, '_')); if (method_exists($this, $method)) { $this->$method($value); } elseif (property_exists($this, $name)) { $this->$name = $value; } } /** * 获取属性 */ public function getProperty(string $name): mixed { $method = 'get' . str_replace('_', '', ucwords($name, '_')); if (method_exists($this, $method)) { return $this->$method(); } return $this->$name ?? null; } /** * 检查属性是否存在 */ public function hasProperty(string $name): bool { return property_exists($this, $name) || method_exists($this, 'get' . str_replace('_', '', ucwords($name, '_'))); } /** * 克隆DTO */ public function clone(): static { return clone $this; } /** * 合并另一个DTO */ public function merge(BaseDto $other): static { $newDto = $this->clone(); $otherData = $other->toArray(); foreach ($otherData as $key => $value) { if ($value !== null) { $newDto->setProperty($key, $value); } } return $newDto; } /** * 魔术方法:转换为字符串 */ public function __toString(): string { return $this->toJson(); } /** * 魔术方法:调试输出 */ public function __debugInfo(): array { return $this->toArray(); } } /** * 必填字段注解 */ #[\Attribute(\Attribute::TARGET_PROPERTY)] class Required { public function __construct(public string $message = '') {} } /** * 字段长度注解 */ #[\Attribute(\Attribute::TARGET_PROPERTY)] class Length { public function __construct(public int $min = 0, public int $max = 255) {} } /** * 字段范围注解 */ #[\Attribute(\Attribute::TARGET_PROPERTY)] class Range { public function __construct(public mixed $min = null, public mixed $max = null) {} } /** * 正则表达式注解 */ #[\Attribute(\Attribute::TARGET_PROPERTY)] class Pattern { public function __construct(public string $regex) {} }