从 PHP 8.2+ 开始,动态属性被弃用。为了代码的健壮性和可维护性,我们要求:
// 错误:直接给未定义的属性赋值(会产生弃用警告)
$this->validation->upgrade_config = $config;
$this->validation->land = $landObject;
// 正确:在 Validation 类中预先定义属性并声明类型
class LandUpgradeValidation extends ValidationCore
{
// 预定义属性,声明类型
public ?FarmLandUpgradeConfig $upgrade_config = null;
public ?FarmLand $land = null;
// 验证规则...
}
?Type)允许初始值为 nullnull)旧的写法(不推荐):
// 在验证器中动态赋值
$this->validation->land = $land;
$this->validation->upgrade_config = $config;
新的写法(推荐):
// 1. 在 Validation 类中预定义
class LandUpgradeValidation extends ValidationCore
{
/** @var FarmLand|null 土地对象,由 LandOwnershipValidator 设置 */
public ?FarmLand $land = null;
/** @var FarmLandUpgradeConfig|null 升级配置,由 LandUpgradePathValidator 设置 */
public ?FarmLandUpgradeConfig $upgrade_config = null;
}
// 2. 在验证器中安全赋值
$this->validation->land = $land;
$this->validation->upgrade_config = $config;
<?php
namespace App\Module\Farm\Validations;
use App\Module\Farm\Models\FarmLand;
use App\Module\Farm\Models\FarmLandUpgradeConfig;
use App\Module\Farm\Validators\LandOwnershipValidator;
use App\Module\Farm\Validators\LandUpgradeStatusValidator;
use App\Module\Farm\Validators\LandUpgradePathValidator;
use UCore\ValidationCore;
/**
* 土地升级验证类
*/
class LandUpgradeValidation extends ValidationCore
{
/** @var FarmLand|null 土地对象,由 LandOwnershipValidator 设置 */
public ?FarmLand $land = null;
/** @var FarmLandUpgradeConfig|null 升级配置,由 LandUpgradePathValidator 设置 */
public ?FarmLandUpgradeConfig $upgrade_config = null;
/**
* 验证规则
*/
public function rules($rules = []): array
{
return [
// 基础验证
['user_id,land_id', 'required'],
['user_id,land_id', 'integer', 'min' => 1],
// 业务验证(按顺序执行)
[
'land_id', new LandOwnershipValidator($this, ['user_id', 'land']),
'msg' => '土地不存在或不属于当前用户'
],
[
'land_id', new LandUpgradeStatusValidator($this, ['land']),
'msg' => '土地状态不允许升级'
],
[
'land_id', new LandUpgradePathValidator($this, ['user_id', 'upgrade_config']),
'msg' => '升级路径验证失败'
],
];
}
/**
* 默认值
*/
public function default(): array
{
return [];
}
}
<?php
namespace App\Module\Farm\Validators;
use App\Module\Farm\Enums\LAND_STATUS;
use App\Module\Farm\Models\FarmLand;
use UCore\Validator;
/**
* 土地归属验证器
*/
class LandOwnershipValidator extends Validator
{
/**
* 验证土地归属
*
* @param mixed $value 土地ID
* @param array $data 包含用户ID的数组
* @return bool 验证是否通过
*/
public function validate(mixed $value, array $data): bool
{
// 从 args 获取参数
$userIdKey = $this->args[0] ?? 'user_id';
$landKey = $this->args[1] ?? 'land';
$userId = $data[$userIdKey] ?? null;
if (!$userId) {
$this->addError('用户ID不能为空');
return false;
}
try {
// 查询土地
$land = FarmLand::where('id', $value)
->where('user_id', $userId)
->first();
if (!$land) {
$this->addError('土地不存在或不属于当前用户');
return false;
}
// 将土地对象保存到验证对象中,供后续验证器使用
$this->validation->$landKey = $land;
return true;
} catch (\Exception $e) {
$this->addError('验证土地归属时发生错误: ' . $e->getMessage());
return false;
}
}
}
<?php
namespace App\Module\Farm\Handlers;
use App\Module\Farm\Validations\LandUpgradeValidation;
use App\Module\Farm\Logics\LandLogic;
use UCore\Handler;
/**
* 土地升级处理器
*/
class LandUpgradeHandler extends Handler
{
/**
* 处理土地升级
*/
public function handle(array $data): array
{
// 1. 验证数据
$validation = new LandUpgradeValidation($data);
$validation->validated();
// 2. 获取验证后的数据和对象
$userId = $validation->getSafe('user_id');
$landId = $validation->getSafe('land_id');
$land = $validation->land; // 预定义的属性,类型安全
$upgradeConfig = $validation->upgrade_config; // 预定义的属性,类型安全
// 3. 执行业务逻辑
DB::transaction(function () use ($userId, $landId, $upgradeConfig) {
$landLogic = new LandLogic();
$landLogic->upgradeLand($userId, $landId, $upgradeConfig->to_type_id);
});
return ['success' => true, 'message' => '土地升级成功'];
}
}
class ExampleValidation extends ValidationCore
{
// ✅ 正确:预定义属性,声明类型,添加注释
/** @var User|null 用户对象,由 UserValidator 设置 */
public ?User $user = null;
/** @var array<Item>|null 物品列表,由 ItemValidator 设置 */
public ?array $items = null;
/** @var Config|null 配置对象,由 ConfigValidator 设置 */
public ?Config $config = null;
// ❌ 错误:没有类型声明
// public $user;
// ❌ 错误:没有注释说明
// public ?User $user = null;
}
class ExampleValidator extends Validator
{
public function validate(mixed $value, array $data): bool
{
// 获取数据
$user = User::find($value);
if (!$user) {
$this->addError('用户不存在');
return false;
}
// ✅ 正确:赋值给预定义的属性
$this->validation->user = $user;
// ❌ 错误:动态属性赋值(会产生弃用警告)
// $this->validation->dynamic_property = $user;
return true;
}
}
class FirstValidator extends Validator
{
public function validate(mixed $value, array $data): bool
{
$user = User::find($value);
// 第一个验证器设置用户对象
$this->validation->user = $user;
return true;
}
}
class SecondValidator extends Validator
{
public function validate(mixed $value, array $data): bool
{
// 第二个验证器使用第一个验证器设置的用户对象
$user = $this->validation->user;
if (!$user) {
$this->addError('用户信息不存在,请先验证用户');
return false;
}
// 使用用户对象进行验证...
return $user->isActive();
}
}
class LandUpgradeValidation extends ValidationCore
{
/** @var FarmLand|null */
public ?FarmLand $land = null;
/** @var FarmLandUpgradeConfig|null */
public ?FarmLandUpgradeConfig $upgrade_config = null;
}
// 在 Handler 中使用
$validation = new LandUpgradeValidation($data);
$validation->validated();
// ✅ IDE 可以提供类型提示和自动完成
$landType = $validation->land->land_type; // IDE 知道这是 FarmLand 对象
$targetType = $validation->upgrade_config->to_type_id; // IDE 知道这是 FarmLandUpgradeConfig 对象
// ✅ 静态分析工具可以检查类型错误
// $validation->land->nonExistentProperty; // IDE 会警告属性不存在
如果你有使用动态属性的现有代码,按以下步骤迁移:
步骤 1:识别动态属性
# 搜索可能的动态属性赋值
grep -r "\$this->validation->" app/Module/*/Validators/
步骤 2:在 Validation 类中定义属性
// 在相应的 Validation 类中添加属性定义
class YourValidation extends ValidationCore
{
/** @var SomeModel|null 描述这个属性的用途 */
public ?SomeModel $property_name = null;
}
步骤 3:更新验证器代码
// 验证器中的赋值保持不变,但现在是类型安全的
$this->validation->property_name = $value;
步骤 4:测试验证
# 运行测试确保没有破坏现有功能
php artisan test
A:
A: 使用 nullable 类型和默认值:
/** @var OptionalData|null 可选数据,可能为 null */
public ?OptionalData $optional_data = null;
A: 使用泛型注释:
/** @var array<Item>|null 物品数组 */
public ?array $items = null;
/** @var array<string, mixed>|null 配置数组 */
public ?array $config = null;
通过遵循这些规范和最佳实践,我们可以:
记住:预定义属性 + 类型声明 + 清晰注释 = 更好的代码