使用trait标记废弃魔术属性时触发Psalm UndefinedDocblockClass报错
问题原因
该报错的核心是Psalm与PhpStorm对@mixin注解的加载逻辑存在差异:PhpStorm会自动索引项目内所有符合PSR规范的类结构,哪怕类/trait没有被实际代码引入,只要写在@mixin里就能识别;但Psalm默认只会扫描被实际代码引用的文件,或是配置中指定的项目目录下的文件,你创建的PHPDoc专用trait没有被实际use引入,也没有被加入Psalm的主动扫描列表,因此会被判定为不存在的Docblock类,和你调整trait的目录层级没有关系。
可落地方案
按实现成本从低到高可选以下三种方案,都能同时满足IDE提示、废弃标记、Psalm校验通过的需求:
- 直接在模型类Docblock标注属性(最简洁)
如果不需要跨模型复用属性标注,完全不需要额外创建trait和@mixin,直接把所有属性(含废弃属性)的标注写在模型类的PHPDoc块上即可,PhpStorm和Psalm都能原生识别废弃删除线提示:<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; /** * @property int $id * @property int $type * @deprecated 请替换为status_text字段使用 * @property string $display_status */ class Product extends Model implements HasLinkableTexts { // 业务代码 } - 实际引入文档专用trait(兼容原有mixin写法)
如果需要在多个模型间复用属性标注,可以保留原有trait结构,只需要在使用@mixin的模型中实际use这个trait即可。注意trait中不要声明真实的类属性,避免和Eloquent的魔术属性取值逻辑冲突,只保留PHPDoc标注即可:
调整后的trait代码:
调整后的模型代码:<?php namespace App\Models\Traits\PhpDoc; /** * @property int $id * @property int $type * @deprecated 请替换为status_text字段使用 * @property string $display_status */ trait ProductPhpDocTrait { // 无实际运行时代码,仅做文档标注用 }
因为trait内没有任何实际逻辑,引入后不会对业务运行产生任何影响。<?php namespace App\Models; use App\Models\Traits\PhpDoc\ProductPhpDocTrait; use Illuminate\Database\Eloquent\Model; /** * @mixin ProductPhpDocTrait */ class Product extends Model implements HasLinkableTexts { use ProductPhpDocTrait; // 实际引入trait,Psalm即可识别 // 业务代码 } - 配置Psalm主动扫描文档trait目录(无运行时代码侵入)
如果不想在业务代码中引入仅用于文档标注的空trait,可以修改项目根目录下的psalm.xml配置,将存放文档标注trait的目录加入Psalm的主动扫描列表,不需要实际use就能让Psalm识别到对应trait的存在:
配置完成后重启Psalm扫描即可消除报错,原有<projectFiles> <!-- 原有项目目录配置 --> <directory name="app/Models/Traits/PhpDoc"/> </projectFiles>@mixin写法不需要做任何修改。
注意:不要在文档专用trait中声明真实的public属性,否则会覆盖Eloquent模型的魔术属性逻辑,导致模型字段取值、赋值出现异常。
内容的提问来源于stack exchange,提问作者Adam Hopkinson
相关产品推荐
相关产品推荐

